MOBILE-3565 core: Delete all files from the project
|
@ -1,2 +0,0 @@
|
||||||
node_modules
|
|
||||||
Dockerfile
|
|
|
@ -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
|
|
|
@ -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
|
|
125
.travis.yml
|
@ -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"
|
|
27
Dockerfile
|
@ -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"]
|
|
|
@ -1,40 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>AD_UNIT_ID_FOR_BANNER_TEST</key>
|
|
||||||
<string></string>
|
|
||||||
<key>AD_UNIT_ID_FOR_INTERSTITIAL_TEST</key>
|
|
||||||
<string></string>
|
|
||||||
<key>CLIENT_ID</key>
|
|
||||||
<string></string>
|
|
||||||
<key>REVERSED_CLIENT_ID</key>
|
|
||||||
<string></string>
|
|
||||||
<key>API_KEY</key>
|
|
||||||
<string></string>
|
|
||||||
<key>GCM_SENDER_ID</key>
|
|
||||||
<string></string>
|
|
||||||
<key>PLIST_VERSION</key>
|
|
||||||
<string>1</string>
|
|
||||||
<key>BUNDLE_ID</key>
|
|
||||||
<string>com.moodle.moodlemobile</string>
|
|
||||||
<key>PROJECT_ID</key>
|
|
||||||
<string>moodlemobile-push</string>
|
|
||||||
<key>STORAGE_BUCKET</key>
|
|
||||||
<string></string>
|
|
||||||
<key>IS_ADS_ENABLED</key>
|
|
||||||
<false></false>
|
|
||||||
<key>IS_ANALYTICS_ENABLED</key>
|
|
||||||
<false></false>
|
|
||||||
<key>IS_APPINVITE_ENABLED</key>
|
|
||||||
<false></false>
|
|
||||||
<key>IS_GCM_ENABLED</key>
|
|
||||||
<false></false>
|
|
||||||
<key>IS_SIGNIN_ENABLED</key>
|
|
||||||
<false></false>
|
|
||||||
<key>GOOGLE_APP_ID</key>
|
|
||||||
<string></string>
|
|
||||||
<key>DATABASE_URL</key>
|
|
||||||
<string></string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
202
LICENSE
|
@ -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.
|
|
|
@ -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 <content src="index.html" /> 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);
|
|
||||||
}
|
|
||||||
}
|
|
13
NOTICE
|
@ -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.
|
|
|
@ -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
|
|
||||||
|
|
648
config.xml
|
@ -1,648 +0,0 @@
|
||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<widget android-versionCode="39300" id="com.moodle.moodlemobile" ios-CFBundleVersion="3.9.3.0" version="3.9.3-dev" versionCode="39300" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
|
||||||
<name>Moodle</name>
|
|
||||||
<description>Moodle official app</description>
|
|
||||||
<author email="mobile@moodle.com" href="http://moodle.com">Moodle Mobile team</author>
|
|
||||||
<content src="index.html" />
|
|
||||||
<access origin="*" />
|
|
||||||
<access launch-external="yes" origin="tel:*" />
|
|
||||||
<access launch-external="yes" origin="mailto:*" />
|
|
||||||
<access launch-external="yes" origin="geo:*" />
|
|
||||||
<allow-navigation href="moodleappfs:*" />
|
|
||||||
<allow-navigation href="cdvfile:*" />
|
|
||||||
<allow-navigation href="content:*" />
|
|
||||||
<allow-navigation href="data:*" />
|
|
||||||
<allow-navigation href="*" />
|
|
||||||
<allow-intent href="*" />
|
|
||||||
<allow-intent href="tel:*" />
|
|
||||||
<allow-intent href="sms:*" />
|
|
||||||
<allow-intent href="mailto:*" />
|
|
||||||
<allow-intent href="geo:*" />
|
|
||||||
<preference name="permissions" value="none" />
|
|
||||||
<preference name="orientation" value="default" />
|
|
||||||
<preference name="target-device" value="universal" />
|
|
||||||
<preference name="fullscreen" value="false" />
|
|
||||||
<preference name="stay-in-webview" value="false" />
|
|
||||||
<preference name="webviewbounce" value="false" />
|
|
||||||
<preference name="UIWebViewBounce" value="false" />
|
|
||||||
<preference name="DisallowOverscroll" value="true" />
|
|
||||||
<preference name="prerendered-icon" value="true" />
|
|
||||||
<preference name="AppendUserAgent" value="MoodleMobile" />
|
|
||||||
<preference name="BackupWebStorage" value="none" />
|
|
||||||
<preference name="ScrollEnabled" value="false" />
|
|
||||||
<preference name="KeyboardDisplayRequiresUserAction" value="false" />
|
|
||||||
<preference name="AllowInlineMediaPlayback" value="true" />
|
|
||||||
<preference name="LoadUrlTimeoutValue" value="60000" />
|
|
||||||
<preference name="load-url-timeout" value="60000" />
|
|
||||||
<preference name="SplashScreen" value="screen" />
|
|
||||||
<preference name="SplashScreenDelay" value="15000" />
|
|
||||||
<preference name="SplashMaintainAspectRatio" value="true" />
|
|
||||||
<preference name="SplashShowOnlyFirstTime" value="false" />
|
|
||||||
<preference name="android-minSdkVersion" value="19" />
|
|
||||||
<preference name="android-targetSdkVersion" value="29" />
|
|
||||||
<preference name="AndroidPersistentFileLocation" value="Compatibility" />
|
|
||||||
<preference name="CustomURLSchemePluginClearsAndroidIntent" value="true" />
|
|
||||||
<preference name="iosPersistentFileLocation" value="Compatibility" />
|
|
||||||
<preference name="iosScheme" value="moodleappfs" />
|
|
||||||
<preference name="WKWebViewOnly" value="true" />
|
|
||||||
<feature name="StatusBar">
|
|
||||||
<param name="ios-package" onload="true" value="CDVStatusBar" />
|
|
||||||
</feature>
|
|
||||||
<platform name="android">
|
|
||||||
<resource-file src="MainActivity.java" target="app/src/main/java/com/moodle/moodlemobile/MainActivity.java" />
|
|
||||||
<resource-file src="google-services.json" target="app/google-services.json" />
|
|
||||||
<resource-file src="resources/android/icon/drawable-ldpi-smallicon.png" target="app/src/main/res/mipmap-ldpi/smallicon.png" />
|
|
||||||
<resource-file src="resources/android/icon/drawable-mdpi-smallicon.png" target="app/src/main/res/mipmap-mdpi/smallicon.png" />
|
|
||||||
<resource-file src="resources/android/icon/drawable-hdpi-smallicon.png" target="app/src/main/res/mipmap-hdpi/smallicon.png" />
|
|
||||||
<resource-file src="resources/android/icon/drawable-xhdpi-smallicon.png" target="app/src/main/res/mipmap-xhdpi/smallicon.png" />
|
|
||||||
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application/activity[@android:name='MainActivity']">
|
|
||||||
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|screenLayout|smallestScreenSize" />
|
|
||||||
</edit-config>
|
|
||||||
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
|
|
||||||
<application android:largeHeap="true" android:usesCleartextTraffic="true" />
|
|
||||||
</edit-config>
|
|
||||||
<config-file parent="/manifest/application" target="AndroidManifest.xml">
|
|
||||||
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="IntentShim">
|
|
||||||
<param name="android-package" value="com.darryncampbell.cordova.plugin.intent.IntentShim" />
|
|
||||||
<param name="onload" value="true" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file mode="merge" parent="/manifest/application" platform="android" target="AndroidManifest.xml">
|
|
||||||
<provider android:authorities="${applicationId}.darryncampbell.cordova.plugin.intent.fileprovider" android:exported="false" android:grantUriPermissions="true" android:name="com.darryncampbell.cordova.plugin.intent.CordovaPluginIntentFileProvider">
|
|
||||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
|
|
||||||
</provider>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="Clipboard">
|
|
||||||
<param name="android-package" value="com.verso.cordova.clipboard.Clipboard" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="CordovaHttpPlugin">
|
|
||||||
<param name="android-package" value="com.silkimen.cordovahttp.CordovaHttpPlugin" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="Camera">
|
|
||||||
<param name="android-package" value="org.apache.cordova.camera.CameraLauncher" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="LaunchMyApp">
|
|
||||||
<param name="android-package" value="nl.xservices.plugins.LaunchMyApp" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="Device">
|
|
||||||
<param name="android-package" value="org.apache.cordova.device.Device" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="File">
|
|
||||||
<param name="android-package" value="org.apache.cordova.file.FileUtils" />
|
|
||||||
<param name="onload" value="true" />
|
|
||||||
</feature>
|
|
||||||
<allow-navigation href="cdvfile:*" />
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="FileOpener2">
|
|
||||||
<param name="android-package" value="io.github.pwlin.cordova.plugins.fileopener2.FileOpener2" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="FileTransfer">
|
|
||||||
<param name="android-package" value="org.apache.cordova.filetransfer.FileTransfer" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="Geolocation">
|
|
||||||
<param name="android-package" value="org.apache.cordova.geolocation.Geolocation" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="Globalization">
|
|
||||||
<param name="android-package" value="org.apache.cordova.globalization.Globalization" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="InAppBrowser">
|
|
||||||
<param name="android-package" value="org.apache.cordova.inappbrowser.InAppBrowser" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="LocalNotification">
|
|
||||||
<param name="android-package" value="de.appplant.cordova.plugin.localnotification.LocalNotification" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/manifest/application" target="AndroidManifest.xml">
|
|
||||||
<provider android:authorities="${applicationId}.localnotifications.provider" android:exported="false" android:grantUriPermissions="true" android:name="de.appplant.cordova.plugin.notification.util.AssetProvider">
|
|
||||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/localnotification_provider_paths" />
|
|
||||||
</provider>
|
|
||||||
<receiver android:exported="false" android:name="de.appplant.cordova.plugin.localnotification.TriggerReceiver" />
|
|
||||||
<receiver android:exported="false" android:name="de.appplant.cordova.plugin.localnotification.ClearReceiver" />
|
|
||||||
<service android:exported="false" android:name="de.appplant.cordova.plugin.localnotification.ClickReceiver" />
|
|
||||||
<receiver android:directBootAware="true" android:exported="false" android:name="de.appplant.cordova.plugin.localnotification.RestoreReceiver">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="Capture">
|
|
||||||
<param name="android-package" value="org.apache.cordova.mediacapture.Capture" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="NetworkStatus">
|
|
||||||
<param name="android-package" value="org.apache.cordova.networkinformation.NetworkManager" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="QRScanner">
|
|
||||||
<param name="android-package" value="com.bitpay.cordova.qrscanner.QRScanner" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="CDVOrientation">
|
|
||||||
<param name="android-package" value="cordova.plugins.screenorientation.CDVOrientation" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="SplashScreen">
|
|
||||||
<param name="android-package" value="org.apache.cordova.splashscreen.SplashScreen" />
|
|
||||||
<param name="onload" value="true" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="StatusBar">
|
|
||||||
<param name="android-package" value="org.apache.cordova.statusbar.StatusBar" />
|
|
||||||
<param name="onload" value="true" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="Whitelist">
|
|
||||||
<param name="android-package" value="org.apache.cordova.whitelist.WhitelistPlugin" />
|
|
||||||
<param name="onload" value="true" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="SQLitePlugin">
|
|
||||||
<param name="android-package" value="io.sqlc.SQLitePlugin" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="PushNotification">
|
|
||||||
<param name="android-package" value="com.adobe.phonegap.push.PushPlugin" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/manifest/application" target="AndroidManifest.xml">
|
|
||||||
<activity android:exported="true" android:name="com.adobe.phonegap.push.PushHandlerActivity" android:permission="${applicationId}.permission.PushHandlerActivity" />
|
|
||||||
<receiver android:name="com.adobe.phonegap.push.BackgroundActionButtonHandler" />
|
|
||||||
<receiver android:name="com.adobe.phonegap.push.PushDismissedHandler" />
|
|
||||||
<service android:name="com.adobe.phonegap.push.FCMService">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
<service android:name="com.adobe.phonegap.push.PushInstanceIDListenerService">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="/*" target="res/xml/config.xml">
|
|
||||||
<feature name="Media">
|
|
||||||
<param name="android-package" value="org.apache.cordova.media.AudioHandler" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
</platform>
|
|
||||||
<platform name="ios">
|
|
||||||
<resource-file src="GoogleService-Info.plist" />
|
|
||||||
<edit-config file="*-Info.plist" mode="merge" target="NSLocationWhenInUseUsageDescription">
|
|
||||||
<string>We need your location so you can attach it as part of your submissions.</string>
|
|
||||||
</edit-config>
|
|
||||||
<edit-config file="*-Info.plist" mode="merge" target="NSLocationAlwaysUsageDescription">
|
|
||||||
<string>We need your location so you can attach it as part of your submissions.</string>
|
|
||||||
</edit-config>
|
|
||||||
<edit-config file="*-Info.plist" mode="merge" target="NSCameraUsageDescription">
|
|
||||||
<string>We need camera access to take pictures so you can attach them as part of your submissions.</string>
|
|
||||||
</edit-config>
|
|
||||||
<edit-config file="*-Info.plist" mode="merge" target="NSMicrophoneUsageDescription">
|
|
||||||
<string>We need microphone access to record sounds so you can attach them as part of your submissions.</string>
|
|
||||||
</edit-config>
|
|
||||||
<edit-config file="*-Info.plist" mode="merge" target="NSPhotoLibraryUsageDescription">
|
|
||||||
<string>We need photo library access to get pictures from there so you can attach them as part of your submissions.</string>
|
|
||||||
</edit-config>
|
|
||||||
<edit-config file="*-Info.plist" mode="merge" target="UISupportsDocumentBrowser">
|
|
||||||
<true />
|
|
||||||
</edit-config>
|
|
||||||
<edit-config file="*-Info.plist" mode="merge" target="CFBundleShortVersionString">
|
|
||||||
<string>3.9.3-dev</string>
|
|
||||||
</edit-config>
|
|
||||||
<config-file parent="FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED" target="*-Info.plist">
|
|
||||||
<string>YES</string>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="UISupportedInterfaceOrientations" target="*-Info.plist">
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="UISupportedInterfaceOrientations~ipad" target="*-Info.plist">
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="UIRequiresFullScreen" target="*-Info.plist">
|
|
||||||
<false />
|
|
||||||
</config-file>
|
|
||||||
<config-file parent="CFBundleDocumentTypes" target="*-Info.plist">
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Unknown File</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.calendar-event</string>
|
|
||||||
<string>public.database</string>
|
|
||||||
<string>public.executable</string>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.content </string>
|
|
||||||
<string>public.item</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Video</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.video</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Image</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.image</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Web Archive</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.apple.webarchive</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>iWork Keynote</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.apple.keynote.key</string>
|
|
||||||
<string>com.apple.iwork.keynote.key</string>
|
|
||||||
<string>com.apple.iwork.keynote.kth</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>iWork Numbers</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.apple.numbers.numbers</string>
|
|
||||||
<string>com.apple.iwork.numbers.numbers</string>
|
|
||||||
<string>com.apple.iwork.numbers.template</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>iWork Pages</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.apple.page.pages</string>
|
|
||||||
<string>com.apple.iwork.pages.pages</string>
|
|
||||||
<string>com.apple.iwork.pages.template</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>OpenDocument Spreadsheet</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>org.oasis.opendocument.spreadsheet</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>OpenDocument Presentation</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>org.oasis.opendocument.presentation</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>OpenDocument Text</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>org.oasis.opendocument.text</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Folder</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.folder</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Audio</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.audio</string>
|
|
||||||
<string>public.mp3</string>
|
|
||||||
<string>public.mpeg-4-audio</string>
|
|
||||||
<string>com.apple.protected-mpeg-4-audio</string>
|
|
||||||
<string>public.aifc-audio</string>
|
|
||||||
<string>com.apple.coreaudio-format</string>
|
|
||||||
<string>public.aiff-audio</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Movie</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.movie</string>
|
|
||||||
<string>public.3gpp2</string>
|
|
||||||
<string>public.3gpp</string>
|
|
||||||
<string>public.mpeg</string>
|
|
||||||
<string>com.apple.quicktime-movie</string>
|
|
||||||
<string>public.mpeg-4</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>GIF image</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.compuserve.gif</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>PNG image</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.png</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>TIFF image</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.tiff</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>JPEG image</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.jpeg</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>XML</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.xml</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>HTML</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.html</string>
|
|
||||||
<string>public.xhtml</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Rich Text</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.rtf</string>
|
|
||||||
<string>com.apple.rtfd</string>
|
|
||||||
<string>com.apple.flat-rtfd</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Text</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.text</string>
|
|
||||||
<string>public.plain-text</string>
|
|
||||||
<string>public.utf8-plain-text</string>
|
|
||||||
<string>public.utf16-external-plain-text</string>
|
|
||||||
<string>public.utf16-plain-text</string>
|
|
||||||
<string>com.apple.traditional-mac-plain-text</string>
|
|
||||||
<string>public.source-code</string>
|
|
||||||
<string>public.c-source</string>
|
|
||||||
<string>public.objective-c-source</string>
|
|
||||||
<string>public.c-plus-plus-source</string>
|
|
||||||
<string>public.objective-c-plus-plus-source</string>
|
|
||||||
<string>public.c-header</string>
|
|
||||||
<string>public.c-plus-plus-header</string>
|
|
||||||
<string>com.sun.java-source</string>
|
|
||||||
<string>public.script</string>
|
|
||||||
<string>public.shell-script</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>zip</string>
|
|
||||||
<string>zipx</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Zip archive</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.zip-archive</string>
|
|
||||||
<string>public.archive</string>
|
|
||||||
<string>com.pkware.zip-archive</string>
|
|
||||||
<string>com.pkware.zipx-archive</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>rar</string>
|
|
||||||
<string>RAR</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Rar archive</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.rarlab.rar-archive</string>
|
|
||||||
<string>public.archive</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>7z</string>
|
|
||||||
<string>7Z</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>7z archive</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>org.7-zip.7-zip-archive</string>
|
|
||||||
<string>public.archive</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Waveform audio</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.microsoft.waveform-audio</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Windows icon image</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.microsoft.ico</string>
|
|
||||||
<string>com.apple.icns</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Windows bitmap image</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.microsoft.bmp</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Microsoft PowerPoint</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.microsoft.powerpoint.ppt</string>
|
|
||||||
<string>org.openxmlformats.presentationml.presentation</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Microsoft Excel</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>org.openxmlformats.spreadsheetml.sheet</string>
|
|
||||||
<string>com.microsoft.excel.xls</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Microsoft Word</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.microsoft.word.doc</string>
|
|
||||||
<string>com.microsoft.word.wordml</string>
|
|
||||||
<string>org.openxmlformats.wordprocessingml.document</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>PDF</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.adobe.pdf</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</config-file>
|
|
||||||
</platform>
|
|
||||||
</widget>
|
|
|
@ -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/'
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -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
|
|
||||||
]
|
|
||||||
};
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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),
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>com.apple.security.app-sandbox</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.inherit</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>com.apple.security.app-sandbox</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>com.apple.security.app-sandbox</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.application-groups</key>
|
|
||||||
<array>
|
|
||||||
<string>2NU57U5PAW.com.moodle.moodledesktop</string>
|
|
||||||
</array>
|
|
||||||
<key>com.apple.security.network.client</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.device.camera</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.files.user-selected.read-only</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.device.audio-input</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
|
@ -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"
|
|
|
@ -1,44 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Package
|
|
||||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
|
||||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
|
||||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities">
|
|
||||||
<Identity Name="3312ADB7.MoodleDesktop"
|
|
||||||
ProcessorArchitecture="x64"
|
|
||||||
Publisher="CN=33CDCDF6-1EB5-4827-9897-ED25C91A32F6"
|
|
||||||
Version="3.9.3.0" />
|
|
||||||
<Properties>
|
|
||||||
<DisplayName>Moodle Desktop</DisplayName>
|
|
||||||
<PublisherDisplayName>Moodle Pty Ltd.</PublisherDisplayName>
|
|
||||||
<Description>The official app for Moodle.</Description>
|
|
||||||
<Logo>assets\StoreLogo.png</Logo>
|
|
||||||
</Properties>
|
|
||||||
<Resources>
|
|
||||||
<Resource Language="en" />
|
|
||||||
</Resources>
|
|
||||||
<Dependencies>
|
|
||||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.14316.0" />
|
|
||||||
</Dependencies>
|
|
||||||
<Capabilities>
|
|
||||||
<rescap:Capability Name="runFullTrust"/>
|
|
||||||
</Capabilities>
|
|
||||||
<Applications>
|
|
||||||
<Application Id="com.moodle.moodlemobile" Executable="app\Moodle Desktop.exe" EntryPoint="Windows.FullTrustApplication">
|
|
||||||
<uap:VisualElements
|
|
||||||
BackgroundColor="#464646"
|
|
||||||
DisplayName="Moodle Desktop"
|
|
||||||
Square150x150Logo="assets\Square150x150Logo.png"
|
|
||||||
Square44x44Logo="assets\Square44x44Logo.png"
|
|
||||||
Description="Moodle Desktop: The official desktop app for Moodle.">
|
|
||||||
<uap:DefaultTile Wide310x150Logo="assets\Wide310x150Logo.png" />
|
|
||||||
</uap:VisualElements>
|
|
||||||
<Extensions>
|
|
||||||
<uap:Extension Category="windows.protocol">
|
|
||||||
<uap:Protocol Name="moodlemobile">
|
|
||||||
<uap:DisplayName>Moodle Mobile URI Scheme</uap:DisplayName>
|
|
||||||
</uap:Protocol>
|
|
||||||
</uap:Extension>
|
|
||||||
</Extensions>
|
|
||||||
</Application>
|
|
||||||
</Applications>
|
|
||||||
</Package>
|
|
|
@ -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));
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
}
|
|
|
@ -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();
|
|
237
gulp/git.js
|
@ -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();
|
|
475
gulp/jira.js
|
@ -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();
|
|
|
@ -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;
|
|
|
@ -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;
|
|
|
@ -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;
|
|
|
@ -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;
|
|
|
@ -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;
|
|
79
gulp/url.js
|
@ -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;
|
|
119
gulp/utils.js
|
@ -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;
|
|
68
gulpfile.js
|
@ -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'));
|
|
||||||
});
|
|
|
@ -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
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"name": "moodlemobile",
|
|
||||||
"integrations": {
|
|
||||||
"cordova": {},
|
|
||||||
"gulp": {}
|
|
||||||
},
|
|
||||||
"type": "ionic-angular",
|
|
||||||
"watchPatterns": [],
|
|
||||||
"id": "com.moodle.moodlemobile"
|
|
||||||
}
|
|
4496
licenses.json
292
package.json
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 932 B |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 55 KiB |
|
@ -1,4 +0,0 @@
|
||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<resources>
|
|
||||||
<color name="background">#FFFFFF</color>
|
|
||||||
</resources>
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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!'
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
//
|
|
||||||
// Moodle is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Moodle is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
//
|
|
||||||
// Moodle is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Moodle is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
|
@ -1,62 +0,0 @@
|
||||||
<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
//
|
|
||||||
// Moodle is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Moodle is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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";
|
|
|
@ -1,474 +0,0 @@
|
||||||
<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
//
|
|
||||||
// Moodle is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Moodle is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper functions converting moodle strings to json.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function detect_languages($languages, $keys) {
|
|
||||||
echo "\n\n\n";
|
|
||||||
|
|
||||||
$all_languages = glob(LANGPACKSFOLDER.'/*' , GLOB_ONLYDIR);
|
|
||||||
function get_lang_from_dir($dir) {
|
|
||||||
return str_replace('_', '-', explode('/', $dir)[3]);
|
|
||||||
}
|
|
||||||
function get_lang_not_wp($langname) {
|
|
||||||
return (substr($langname, -3) !== '-wp');
|
|
||||||
}
|
|
||||||
$all_languages = array_map('get_lang_from_dir', $all_languages);
|
|
||||||
$all_languages = array_filter($all_languages, 'get_lang_not_wp');
|
|
||||||
|
|
||||||
$detect_lang = array_diff($all_languages, $languages);
|
|
||||||
$new_langs = array();
|
|
||||||
foreach ($detect_lang as $lang) {
|
|
||||||
reset_translations_strings();
|
|
||||||
$new = detect_lang($lang, $keys);
|
|
||||||
if ($new) {
|
|
||||||
$new_langs[$lang] = $lang;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $new_langs;
|
|
||||||
}
|
|
||||||
|
|
||||||
function build_languages($languages, $keys, $added_langs = []) {
|
|
||||||
// Process the languages.
|
|
||||||
foreach ($languages as $lang) {
|
|
||||||
reset_translations_strings();
|
|
||||||
$ok = build_lang($lang, $keys);
|
|
||||||
if ($ok) {
|
|
||||||
$added_langs[$lang] = $lang;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $added_langs;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_langindex_keys() {
|
|
||||||
$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 = '<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
//
|
|
||||||
// Moodle is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Moodle is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Version details.
|
|
||||||
*
|
|
||||||
* @package local
|
|
||||||
* @subpackage moodlemobileapp
|
|
||||||
* @copyright 2014 Juan Leyva <juanleyvadelgado@gmail.com>
|
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
||||||
*/
|
|
||||||
|
|
||||||
$string[\'appstoredescription\'] = \'NOTE: This official Moodle Mobile app will ONLY work with Moodle sites that have been set up to allow it. Please talk to your Moodle administrator if you have any problems connecting.
|
|
||||||
|
|
||||||
If your Moodle site has been configured correctly, you can use this app to:
|
|
||||||
|
|
||||||
- browse the content of your courses, even when offline
|
|
||||||
- receive instant notifications of messages and other events
|
|
||||||
- quickly find and contact other people in your courses
|
|
||||||
- upload images, audio, videos and other files from your mobile device
|
|
||||||
- view your course grades
|
|
||||||
- and more!
|
|
||||||
|
|
||||||
Please see http://docs.moodle.org/en/Mobile_app for all the latest information.
|
|
||||||
|
|
||||||
We’d really appreciate any good reviews about the functionality so far, and your suggestions on what else you want this app to do!
|
|
||||||
|
|
||||||
The app requires the following permissions:
|
|
||||||
Record audio - For recording audio to upload to Moodle
|
|
||||||
Read and modify the contents of your SD card - Contents are downloaded to the SD Card so you can see them offline
|
|
||||||
Network access - To be able to connect with your Moodle site and check if you are connected or not to switch to offline mode
|
|
||||||
Run at startup - So you receive local notifications even when the app is running in the background
|
|
||||||
Prevent phone from sleeping - So you can receive push notifications anytime\';'."\n";
|
|
||||||
foreach ($keys as $key => $value) {
|
|
||||||
if (isset($translations[$key]) && $value->file == 'local_moodlemobileapp') {
|
|
||||||
$string .= '$string[\''.$key.'\'] = \''.str_replace("'", "\'", $translations[$key]).'\';'."\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$string .= '$string[\'pluginname\'] = \'Moodle Mobile language strings\';'."\n";
|
|
||||||
|
|
||||||
file_put_contents('../../moodle-local_moodlemobileapp/lang/en/local_moodlemobileapp.php', $string."\n");
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -1,62 +0,0 @@
|
||||||
<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
//
|
|
||||||
// Moodle is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Moodle is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
|
@ -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
|
|
|
@ -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!'
|
|
|
@ -1,244 +0,0 @@
|
||||||
<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
//
|
|
||||||
// Moodle is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Moodle is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper functions 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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!)"
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
<ion-header>
|
|
||||||
<ion-navbar core-back-button>
|
|
||||||
<ion-title>{{badge && badge.name}}</ion-title>
|
|
||||||
</ion-navbar>
|
|
||||||
</ion-header>
|
|
||||||
<ion-content>
|
|
||||||
<ion-refresher [enabled]="badgeLoaded" (ionRefresh)="refreshBadges($event)">
|
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
|
||||||
</ion-refresher>
|
|
||||||
<core-loading [hideUntil]="badgeLoaded">
|
|
||||||
|
|
||||||
<ion-item-group *ngIf="badge">
|
|
||||||
<ion-item text-wrap class="item-avatar-center">
|
|
||||||
<img *ngIf="badge.badgeurl" class="avatar" [src]="badge.badgeurl" core-external-content [alt]="badge.name">
|
|
||||||
<ion-badge color="danger" *ngIf="badge.dateexpire && currentTime >= badge.dateexpire">
|
|
||||||
{{ 'addon.badges.expired' | translate }}
|
|
||||||
</ion-badge>
|
|
||||||
</ion-item>
|
|
||||||
</ion-item-group>
|
|
||||||
|
|
||||||
<ion-item-group *ngIf="user.fullname">
|
|
||||||
<ion-item-divider>
|
|
||||||
<h2>{{ 'addon.badges.recipientdetails' | translate}}</h2>
|
|
||||||
</ion-item-divider>
|
|
||||||
<ion-item text-wrap>
|
|
||||||
<h2>{{ 'core.name' | translate}}</h2>
|
|
||||||
<p>{{ user.fullname }}</p>
|
|
||||||
</ion-item>
|
|
||||||
</ion-item-group>
|
|
||||||
|
|
||||||
<ion-item-group *ngIf="badge">
|
|
||||||
<ion-item-divider>
|
|
||||||
<h2>{{ 'addon.badges.issuerdetails' | translate}}</h2>
|
|
||||||
</ion-item-divider>
|
|
||||||
<ion-item text-wrap *ngIf="badge.issuername">
|
|
||||||
<h2>{{ 'addon.badges.issuername' | translate}}</h2>
|
|
||||||
<p>{{ badge.issuername }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.issuercontact">
|
|
||||||
<h2>{{ 'addon.badges.contact' | translate}}</h2>
|
|
||||||
<p><a href="mailto:{{badge.issuercontact}}" core-link auto-login="no">
|
|
||||||
{{ badge.issuercontact }}
|
|
||||||
</a></p>
|
|
||||||
</ion-item>
|
|
||||||
</ion-item-group>
|
|
||||||
|
|
||||||
<ion-item-group *ngIf="badge">
|
|
||||||
<ion-item-divider>
|
|
||||||
<h2>{{ 'addon.badges.badgedetails' | translate}}</h2>
|
|
||||||
</ion-item-divider>
|
|
||||||
<ion-item text-wrap *ngIf="badge.name">
|
|
||||||
<h2>{{ 'core.name' | translate}}</h2>
|
|
||||||
<p>{{ badge.name }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.version">
|
|
||||||
<h2>{{ 'addon.badges.version' | translate}}</h2>
|
|
||||||
<p>{{ badge.version }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.language">
|
|
||||||
<h2>{{ 'addon.badges.language' | translate}}</h2>
|
|
||||||
<p>{{ badge.language }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.description">
|
|
||||||
<h2>{{ 'core.description' | translate}}</h2>
|
|
||||||
<p>{{ badge.description }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.imageauthorname">
|
|
||||||
<h2>{{ 'addon.badges.imageauthorname' | translate}}</h2>
|
|
||||||
<p>{{ badge.imageauthorname }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.imageauthoremail">
|
|
||||||
<h2>{{ 'addon.badges.imageauthoremail' | translate}}</h2>
|
|
||||||
<p><a href="mailto:{{badge.imageauthoremail}}" core-link auto-login="no">
|
|
||||||
{{ badge.imageauthoremail }}
|
|
||||||
</a></p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.imageauthorurl">
|
|
||||||
<h2>{{ 'addon.badges.imageauthorurl' | translate}}</h2>
|
|
||||||
<p><a [href]="badge.imageauthorurl" core-link auto-login="no">
|
|
||||||
{{ badge.imageauthorurl }}
|
|
||||||
</a></p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.imagecaption">
|
|
||||||
<h2>{{ 'addon.badges.imagecaption' | translate}}</h2>
|
|
||||||
<p>{{ badge.imagecaption }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="course.fullname">
|
|
||||||
<h2>{{ 'core.course' | translate}}</h2>
|
|
||||||
<p>
|
|
||||||
<core-format-text [text]="course.fullname" contextLevel="course" [contextInstanceId]="courseId"></core-format-text>
|
|
||||||
</p>
|
|
||||||
</ion-item>
|
|
||||||
<!-- Criteria (not yet avalaible) -->
|
|
||||||
</ion-item-group>
|
|
||||||
|
|
||||||
<ion-item-group *ngIf="badge">
|
|
||||||
<ion-item-divider>
|
|
||||||
<h2>{{ 'addon.badges.issuancedetails' | translate}}</h2>
|
|
||||||
</ion-item-divider>
|
|
||||||
<ion-item text-wrap *ngIf="badge.dateissued">
|
|
||||||
<h2>{{ 'addon.badges.dateawarded' | translate}}</h2>
|
|
||||||
<p>{{badge.dateissued * 1000 | coreFormatDate }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.dateexpire">
|
|
||||||
<h2>{{ 'addon.badges.expirydate' | translate}}</h2>
|
|
||||||
<p>
|
|
||||||
{{ badge.dateexpire * 1000 | coreFormatDate }}
|
|
||||||
<span class="text-danger" *ngIf="currentTime >= badge.dateexpire">
|
|
||||||
{{ 'addon.badges.warnexpired' | translate }}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</ion-item>
|
|
||||||
<!-- Evidence (not yet avalaible) -->
|
|
||||||
</ion-item-group>
|
|
||||||
|
|
||||||
<!-- Endorsement -->
|
|
||||||
<ion-item-group *ngIf="badge && badge.endorsement">
|
|
||||||
<ion-item-divider>
|
|
||||||
<h2>{{ 'addon.badges.bendorsement' | translate}}</h2>
|
|
||||||
</ion-item-divider>
|
|
||||||
<ion-item text-wrap *ngIf="badge.endorsement.issuername">
|
|
||||||
<h2>{{ 'addon.badges.issuername' | translate}}</h2>
|
|
||||||
<p>{{ badge.endorsement.issuername }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.endorsement.issueremail">
|
|
||||||
<h2>{{ 'addon.badges.issueremail' | translate}}</h2>
|
|
||||||
<p><a href="mailto:{{badge.endorsement.issueremail}}" core-link auto-login="no">
|
|
||||||
{{ badge.endorsement.issueremail }}
|
|
||||||
</a></p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.endorsement.issuerurl">
|
|
||||||
<h2>{{ 'addon.badges.issuerurl' | translate}}</h2>
|
|
||||||
<p><a [href]="badge.endorsement.issuerurl" core-link auto-login="no">
|
|
||||||
{{ badge.endorsement.issuerurl }}
|
|
||||||
</a></p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.endorsement.dateissued">
|
|
||||||
<h2>{{ 'addon.badges.dateawarded' | translate}}</h2>
|
|
||||||
<p>{{ badge.endorsement.dateissued * 1000 | coreFormatDate }}</p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.endorsement.claimid">
|
|
||||||
<h2>{{ 'addon.badges.claimid' | translate}}</h2>
|
|
||||||
<p><a [href]="badge.endorsement.claimid" core-link auto-login="no">
|
|
||||||
{{ badge.endorsement.claimid }}
|
|
||||||
</a></p>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.endorsement.claimcomment">
|
|
||||||
<h2>{{ 'addon.badges.claimcomment' | translate}}</h2>
|
|
||||||
<p>{{ badge.endorsement.claimcomment }}</p>
|
|
||||||
</ion-item>
|
|
||||||
</ion-item-group>
|
|
||||||
|
|
||||||
<!-- Related badges -->
|
|
||||||
<ion-item-group *ngIf="badge && badge.relatedbadges">
|
|
||||||
<ion-item-divider>
|
|
||||||
<h2>{{ 'addon.badges.relatedbages' | translate}}</h2>
|
|
||||||
</ion-item-divider>
|
|
||||||
<ion-item text-wrap *ngFor="let relatedBadge of badge.relatedbadges">
|
|
||||||
<h2>{{ relatedBadge.name }}</h2>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item text-wrap *ngIf="badge.relatedbadges.length == 0">
|
|
||||||
<h2>{{ 'addon.badges.norelated' | translate}}</h2>
|
|
||||||
</ion-item>
|
|
||||||
</ion-item-group>
|
|
||||||
|
|
||||||
<!-- Competencies alignment -->
|
|
||||||
<ion-item-group *ngIf="badge && badge.alignment">
|
|
||||||
<ion-item-divider>
|
|
||||||
<h2>{{ 'addon.badges.alignment' | translate}}</h2>
|
|
||||||
</ion-item-divider>
|
|
||||||
<a ion-item text-wrap *ngFor="let alignment of badge.alignment" [href]="alignment.targeturl" core-link auto-login="no">
|
|
||||||
<h2>{{ alignment.targetname }}</h2>
|
|
||||||
</a>
|
|
||||||
<ion-item text-wrap *ngIf="badge.alignment.length == 0">
|
|
||||||
<h2>{{ 'addon.badges.noalignment' | translate}}</h2>
|
|
||||||
</ion-item>
|
|
||||||
</ion-item-group>
|
|
||||||
</core-loading>
|
|
||||||
</ion-content>
|
|
|
@ -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 {}
|
|
|
@ -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<any> {
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
<ion-header>
|
|
||||||
<ion-navbar core-back-button>
|
|
||||||
<ion-title>{{ 'addon.badges.badges' | translate }}</ion-title>
|
|
||||||
</ion-navbar>
|
|
||||||
</ion-header>
|
|
||||||
<core-split-view>
|
|
||||||
<ion-content>
|
|
||||||
<ion-refresher [enabled]="badgesLoaded" (ionRefresh)="refreshBadges($event)">
|
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
|
||||||
</ion-refresher>
|
|
||||||
<core-loading [hideUntil]="badgesLoaded">
|
|
||||||
<core-empty-box *ngIf="!badges || badges.length == 0" icon="trophy" [message]="'addon.badges.nobadges' | translate">
|
|
||||||
</core-empty-box>
|
|
||||||
|
|
||||||
<ion-list *ngIf="badges && badges.length" no-margin>
|
|
||||||
<a ion-item text-wrap *ngFor="let badge of badges" [title]="badge.name" (click)="loadIssuedBadge(badge.uniquehash)" [class.core-split-item-selected]="badge.uniquehash == badgeHash">
|
|
||||||
<ion-avatar item-start>
|
|
||||||
<img [src]="badge.badgeurl" [alt]="badge.name" item-start core-external-content>
|
|
||||||
</ion-avatar>
|
|
||||||
<h2>{{ badge.name }}</h2>
|
|
||||||
<p>{{ badge.dateissued * 1000 | coreFormatDate :'strftimedatetimeshort' }}</p>
|
|
||||||
<ion-badge item-end color="danger" *ngIf="badge.dateexpire && currentTime >= badge.dateexpire">
|
|
||||||
{{ 'addon.badges.expired' | translate }}
|
|
||||||
</ion-badge>
|
|
||||||
</a>
|
|
||||||
</ion-list>
|
|
||||||
</core-loading>
|
|
||||||
</ion-content>
|
|
||||||
</core-split-view>
|
|
|
@ -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 {}
|
|
|
@ -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<any> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<CoreContentLinksAction[]> {
|
|
||||||
|
|
||||||
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<boolean> {
|
|
||||||
|
|
||||||
return this.badgesProvider.isPluginEnabled(siteId);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<boolean> {
|
|
||||||
|
|
||||||
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<AddonBadgesUserBadge[]> {
|
|
||||||
|
|
||||||
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<any> {
|
|
||||||
|
|
||||||
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.
|
|
||||||
}[];
|
|
||||||
};
|
|
|
@ -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<CoreContentLinksAction[]> {
|
|
||||||
|
|
||||||
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<boolean> {
|
|
||||||
|
|
||||||
return this.badgesProvider.isPluginEnabled(siteId);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<boolean> {
|
|
||||||
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<any> {
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<boolean> {
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<any> {
|
|
||||||
return this.courseProvider.invalidateSections(this.instanceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch the data to render the block.
|
|
||||||
*
|
|
||||||
* @return Promise resolved when done.
|
|
||||||
*/
|
|
||||||
protected fetchContent(): Promise<any> {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
<ion-item-divider>
|
|
||||||
<h2>{{ 'addon.block_activitymodules.pluginname' | translate }}</h2>
|
|
||||||
</ion-item-divider>
|
|
||||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
|
||||||
<a ion-item text-wrap *ngFor="let entry of entries" class="item-media" detail-none navPush="CoreCourseListModTypePage" [navParams]="{title: entry.name, courseId: instanceId, modName: entry.modName}">
|
|
||||||
<img item-start [src]="entry.icon" alt="" role="presentation" class="core-module-icon">
|
|
||||||
{{ entry.name }}
|
|
||||||
</a>
|
|
||||||
</core-loading>
|
|
|
@ -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 {}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"pluginname": "Activities"
|
|
||||||
}
|
|
|
@ -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<CoreBlockHandlerData> {
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: 'addon.block_activitymodules.pluginname',
|
|
||||||
class: 'addon-block-activitymodules',
|
|
||||||
component: AddonBlockActivityModulesComponent
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"pluginname": "Activity results"
|
|
||||||
}
|
|
|
@ -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<CoreBlockHandlerData> {
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: 'addon.block_activityresults.pluginname',
|
|
||||||
class: 'addon-block-activity-results',
|
|
||||||
component: CoreBlockPreRenderedComponent
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"pluginname": "Latest badges"
|
|
||||||
}
|
|
|
@ -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<CoreBlockHandlerData> {
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: 'addon.block_badges.pluginname',
|
|
||||||
class: 'addon-block-badges',
|
|
||||||
component: CoreBlockPreRenderedComponent
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"pluginname": "Blog menu"
|
|
||||||
}
|
|
|
@ -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<CoreBlockHandlerData> {
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: 'addon.block_blogmenu.pluginname',
|
|
||||||
class: 'addon-block-blog-menu',
|
|
||||||
component: CoreBlockPreRenderedComponent
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|