mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-02-04 06:22:59 -05:00
all
This commit is contained in:
33
lms-plugins/mediacms-moodle/tiny/build_instructions.md
Normal file
33
lms-plugins/mediacms-moodle/tiny/build_instructions.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# JavaScript Build Instructions
|
||||||
|
|
||||||
|
These instructions explain how to manually rebuild the JavaScript modules for the TinyMCE plugin. Moodle requires AMD modules, but the source code is written in ES6.
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
* Node.js 16+ (Node 22 recommended)
|
||||||
|
|
||||||
|
### Build Steps
|
||||||
|
|
||||||
|
1. **Navigate to the package root:**
|
||||||
|
```bash
|
||||||
|
cd lms-plugins/mediacms-moodle
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Initialize dependencies:**
|
||||||
|
(Only needed the first time)
|
||||||
|
```bash
|
||||||
|
npm init -y
|
||||||
|
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-modules-amd
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Run the Build:**
|
||||||
|
This command uses the local Babel binary to avoid version conflicts and transpiles the code to AMD format.
|
||||||
|
```bash
|
||||||
|
./node_modules/.bin/babel tiny/mediacms/amd/src --out-dir tiny/mediacms/amd/build --presets=@babel/preset-env --plugins=@babel/plugin-transform-modules-amd
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Minify (Optional but Recommended):**
|
||||||
|
Moodle loads `.min.js` files by default. This creates copies for production use.
|
||||||
|
```bash
|
||||||
|
cd tiny/mediacms/amd/build
|
||||||
|
for f in *.js; do cp "$f" "${f%.js}.min.js"; done
|
||||||
|
```
|
||||||
206
lms-plugins/mediacms-moodle/tiny/mediacms/AUTOCONVERT.md
Executable file
206
lms-plugins/mediacms-moodle/tiny/mediacms/AUTOCONVERT.md
Executable file
@@ -0,0 +1,206 @@
|
|||||||
|
# MediaCMS URL Auto-Convert Feature
|
||||||
|
|
||||||
|
This feature automatically converts pasted MediaCMS video URLs into embedded video players within the TinyMCE editor.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
When a user pastes a MediaCMS video URL like:
|
||||||
|
```
|
||||||
|
https://deic.mediacms.io/view?m=JpBd1Zvdl
|
||||||
|
```
|
||||||
|
|
||||||
|
It is automatically converted to an embedded video player:
|
||||||
|
```html
|
||||||
|
<div class="tiny-iframe-responsive" contenteditable="false">
|
||||||
|
<iframe
|
||||||
|
style="width: 100%; max-width: calc(100vh * 16 / 9); aspect-ratio: 16 / 9; display: block; margin: auto; border: 0;"
|
||||||
|
src="https://deic.mediacms.io/embed?m=JpBd1Zvdl&showTitle=1&showRelated=1&showUserAvatar=1&linkTitle=1"
|
||||||
|
allowfullscreen="allowfullscreen">
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported URL Formats
|
||||||
|
|
||||||
|
The auto-convert feature recognizes MediaCMS view URLs in this format:
|
||||||
|
- `https://[domain]/view?m=[VIDEO_ID]`
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- `https://deic.mediacms.io/view?m=JpBd1Zvdl`
|
||||||
|
- `https://your-mediacms-instance.com/view?m=abc123`
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Accessing Settings
|
||||||
|
|
||||||
|
1. Log in to Moodle as an administrator
|
||||||
|
2. Navigate to: **Site administration** → **Plugins** → **Text editors** → **TinyMCE editor** → **MediaCMS**
|
||||||
|
3. Scroll to the **Auto-convert MediaCMS URLs** section
|
||||||
|
|
||||||
|
### Available Settings
|
||||||
|
|
||||||
|
| Setting | Description | Default |
|
||||||
|
|---------|-------------|---------|
|
||||||
|
| **Enable auto-convert** | Turn the auto-convert feature on or off | Enabled |
|
||||||
|
| **MediaCMS base URL** | Restrict auto-conversion to a specific MediaCMS domain | Empty (allow all) |
|
||||||
|
| **Show video title** | Display the video title in the embedded player | Enabled |
|
||||||
|
| **Link video title** | Make the video title clickable, linking to the original video page | Enabled |
|
||||||
|
| **Show related videos** | Display related videos after the current video ends | Enabled |
|
||||||
|
| **Show user avatar** | Display the uploader's avatar in the embedded player | Enabled |
|
||||||
|
|
||||||
|
### Settings Location in Moodle
|
||||||
|
|
||||||
|
The settings are stored in the Moodle database under the `tiny_mediacms` plugin configuration:
|
||||||
|
|
||||||
|
- `tiny_mediacms/autoconvertenabled` - Enable/disable auto-convert
|
||||||
|
- `tiny_mediacms/autoconvert_baseurl` - MediaCMS base URL (e.g., https://deic.mediacms.io)
|
||||||
|
- `tiny_mediacms/autoconvert_showtitle` - Show title option
|
||||||
|
- `tiny_mediacms/autoconvert_linktitle` - Link title option
|
||||||
|
- `tiny_mediacms/autoconvert_showrelated` - Show related option
|
||||||
|
- `tiny_mediacms/autoconvert_showuseravatar` - Show user avatar option
|
||||||
|
|
||||||
|
### Base URL Configuration
|
||||||
|
|
||||||
|
The **MediaCMS base URL** setting controls which MediaCMS instances are recognized for auto-conversion:
|
||||||
|
|
||||||
|
- **Empty (default)**: Any MediaCMS URL will be auto-converted (e.g., URLs from any `*/view?m=*` pattern)
|
||||||
|
- **Specific URL**: Only URLs from the specified domain will be auto-converted
|
||||||
|
|
||||||
|
Example configurations:
|
||||||
|
- `https://deic.mediacms.io` - Only convert URLs from deic.mediacms.io
|
||||||
|
- `https://media.myuniversity.edu` - Only convert URLs from your institution's MediaCMS
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
amd/src/
|
||||||
|
├── autoconvert.js # Main auto-convert module
|
||||||
|
├── plugin.js # Plugin initialization (imports autoconvert)
|
||||||
|
└── options.js # Configuration options definition
|
||||||
|
|
||||||
|
classes/
|
||||||
|
└── plugininfo.php # Passes PHP settings to JavaScript
|
||||||
|
|
||||||
|
settings.php # Admin settings page definition
|
||||||
|
|
||||||
|
lang/en/
|
||||||
|
└── tiny_mediacms.php # Language strings for settings
|
||||||
|
```
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
1. **Paste Detection**: The `autoconvert.js` module listens for `paste` events on the TinyMCE editor
|
||||||
|
2. **URL Validation**: When text is pasted, it checks if it matches the MediaCMS URL pattern
|
||||||
|
3. **HTML Generation**: If valid, it generates the responsive iframe HTML with configured options
|
||||||
|
4. **Content Insertion**: The original URL is replaced with the embedded video
|
||||||
|
|
||||||
|
### JavaScript Configuration
|
||||||
|
|
||||||
|
The settings are passed from PHP to JavaScript via the `plugininfo.php` class:
|
||||||
|
|
||||||
|
```php
|
||||||
|
protected static function get_autoconvert_configuration(): array {
|
||||||
|
$baseurl = get_config('tiny_mediacms', 'autoconvert_baseurl');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'data' => [
|
||||||
|
'autoConvertEnabled' => (bool) get_config('tiny_mediacms', 'autoconvertenabled'),
|
||||||
|
'autoConvertBaseUrl' => !empty($baseurl) ? $baseurl : '',
|
||||||
|
'autoConvertOptions' => [
|
||||||
|
'showTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_showtitle'),
|
||||||
|
'linkTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_linktitle'),
|
||||||
|
'showRelated' => (bool) get_config('tiny_mediacms', 'autoconvert_showrelated'),
|
||||||
|
'showUserAvatar' => (bool) get_config('tiny_mediacms', 'autoconvert_showuseravatar'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default Values (in options.js)
|
||||||
|
|
||||||
|
If PHP settings are not configured, the JavaScript uses these defaults:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
registerOption(dataName, {
|
||||||
|
processor: 'object',
|
||||||
|
"default": {
|
||||||
|
autoConvertEnabled: true,
|
||||||
|
autoConvertBaseUrl: '', // Empty = allow all MediaCMS domains
|
||||||
|
autoConvertOptions: {
|
||||||
|
showTitle: true,
|
||||||
|
linkTitle: true,
|
||||||
|
showRelated: true,
|
||||||
|
showUserAvatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
### Disabling Auto-Convert
|
||||||
|
|
||||||
|
To disable the feature entirely:
|
||||||
|
1. Go to the plugin settings (see "Accessing Settings" above)
|
||||||
|
2. Uncheck **Enable auto-convert**
|
||||||
|
3. Save changes
|
||||||
|
|
||||||
|
### Programmatic Configuration
|
||||||
|
|
||||||
|
You can also set these values directly in the database using Moodle's `set_config()` function:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Disable auto-convert
|
||||||
|
set_config('autoconvertenabled', 0, 'tiny_mediacms');
|
||||||
|
|
||||||
|
// Set the MediaCMS base URL (restrict to specific domain)
|
||||||
|
set_config('autoconvert_baseurl', 'https://deic.mediacms.io', 'tiny_mediacms');
|
||||||
|
|
||||||
|
// Customize embed options
|
||||||
|
set_config('autoconvert_showtitle', 1, 'tiny_mediacms');
|
||||||
|
set_config('autoconvert_linktitle', 0, 'tiny_mediacms');
|
||||||
|
set_config('autoconvert_showrelated', 0, 'tiny_mediacms');
|
||||||
|
set_config('autoconvert_showuseravatar', 1, 'tiny_mediacms');
|
||||||
|
```
|
||||||
|
|
||||||
|
### CLI Configuration
|
||||||
|
|
||||||
|
Using Moodle CLI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable auto-convert
|
||||||
|
php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvertenabled --set=1
|
||||||
|
|
||||||
|
# Set the MediaCMS base URL
|
||||||
|
php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_baseurl --set=https://deic.mediacms.io
|
||||||
|
|
||||||
|
# Disable showing related videos
|
||||||
|
php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_showrelated --set=0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Auto-convert not working
|
||||||
|
|
||||||
|
1. **Check if enabled**: Verify the setting is enabled in plugin settings
|
||||||
|
2. **Clear caches**: Purge all caches (Site administration → Development → Purge all caches)
|
||||||
|
3. **Check URL format**: Ensure the URL matches the pattern `https://[domain]/view?m=[VIDEO_ID]`
|
||||||
|
4. **Browser console**: Check for JavaScript errors in the browser developer console
|
||||||
|
|
||||||
|
### Rebuilding JavaScript
|
||||||
|
|
||||||
|
If you modify the source files, rebuild using:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/moodle
|
||||||
|
npx grunt amd --root=public/lib/editor/tiny/plugins/mediacms
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Requires Node.js 22.x or compatible version as specified in Moodle's requirements.
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
- **1.0.0** - Initial implementation of auto-convert feature
|
||||||
268
lms-plugins/mediacms-moodle/tiny/mediacms/LTI_INTEGRATION.md
Executable file
268
lms-plugins/mediacms-moodle/tiny/mediacms/LTI_INTEGRATION.md
Executable file
@@ -0,0 +1,268 @@
|
|||||||
|
# MediaCMS LTI Integration Guide
|
||||||
|
|
||||||
|
This document explains how the TinyMCE MediaCMS plugin integrates with Moodle's LTI (Learning Tools Interoperability) system to provide authenticated access to the MediaCMS video library.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The plugin uses **LTI 1.3 Deep Linking** to authenticate users with MediaCMS. Instead of implementing LTI authentication directly, the plugin leverages Moodle's built-in LTI module (`/mod/lti/`) to handle the complex OIDC/JWT authentication flow.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ MOODLE (Your Instance) │
|
||||||
|
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │
|
||||||
|
│ │ TinyMCE Editor │ │ /mod/lti/ │ │ External Tool │ │
|
||||||
|
│ │ MediaCMS Plugin │───▶│ contentitem.php │───▶│ Configuration │ │
|
||||||
|
│ │ (this plugin) │ │ auth.php │ │ (Tool ID: X) │ │
|
||||||
|
│ └─────────────────────┘ └─────────────────────┘ └─────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
│ LTI 1.3 OIDC Flow
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ MEDIACMS (External Service) │
|
||||||
|
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │
|
||||||
|
│ │ OIDC Login │ │ LTI Launch │ │ Video Library │ │
|
||||||
|
│ │ /lti/oidc/login/ │───▶│ /lti/launch/ │───▶│ /lti/select/ │ │
|
||||||
|
│ └─────────────────────┘ └─────────────────────┘ └─────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
tiny/plugins/mediacms/
|
||||||
|
├── classes/
|
||||||
|
│ └── plugininfo.php # Backend: Passes LTI config to JavaScript
|
||||||
|
├── amd/src/
|
||||||
|
│ ├── options.js # Frontend: Registers LTI options
|
||||||
|
│ └── iframeembed.js # Frontend: Implements LTI form submission
|
||||||
|
├── settings.php # Admin settings (LTI Tool ID)
|
||||||
|
├── lang/en/
|
||||||
|
│ └── tiny_mediacms.php # Language strings
|
||||||
|
└── LTI_INTEGRATION.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Step 1: Admin Configuration
|
||||||
|
|
||||||
|
An administrator must configure the LTI Tool ID in:
|
||||||
|
**Site Administration → Plugins → Text editors → TinyMCE → MediaCMS settings**
|
||||||
|
|
||||||
|
The Tool ID corresponds to an External Tool configured in Moodle that points to MediaCMS.
|
||||||
|
|
||||||
|
### Step 2: Backend Preparation (plugininfo.php)
|
||||||
|
|
||||||
|
When the editor loads, `get_lti_configuration()` retrieves:
|
||||||
|
- **toolId**: The External Tool ID from plugin settings
|
||||||
|
- **courseId**: The current course context
|
||||||
|
- **contentItemUrl**: URL to Moodle's `/mod/lti/contentitem.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
protected static function get_lti_configuration(context $context): array {
|
||||||
|
$ltitoolid = get_config('tiny_mediacms', 'ltitoolid');
|
||||||
|
// ... determine courseId from context ...
|
||||||
|
return [
|
||||||
|
'lti' => [
|
||||||
|
'toolId' => (int) $ltitoolid,
|
||||||
|
'courseId' => $courseid,
|
||||||
|
'contentItemUrl' => '/mod/lti/contentitem.php',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Frontend LTI Launch (iframeembed.js)
|
||||||
|
|
||||||
|
When the user clicks "Video Library from Iframe" tab:
|
||||||
|
|
||||||
|
1. **Check LTI config**: If `toolId`, `courseId`, and `contentItemUrl` are set, use LTI flow
|
||||||
|
2. **Create hidden form**: Dynamically create an HTML form targeting the iframe
|
||||||
|
3. **Submit to Moodle LTI**: POST to `/mod/lti/contentitem.php` with tool parameters
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
loadIframeLibraryViaLti(root, ltiConfig) {
|
||||||
|
// Create form targeting the iframe
|
||||||
|
const ltiForm = document.createElement('form');
|
||||||
|
ltiForm.target = iframeName;
|
||||||
|
ltiForm.action = ltiConfig.contentItemUrl;
|
||||||
|
ltiForm.method = 'post';
|
||||||
|
|
||||||
|
// Add LTI parameters
|
||||||
|
const fields = {
|
||||||
|
'id': ltiConfig.toolId,
|
||||||
|
'course': ltiConfig.courseId,
|
||||||
|
'title': '',
|
||||||
|
'text': '',
|
||||||
|
};
|
||||||
|
// ... create hidden inputs and submit ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: LTI Authentication Flow
|
||||||
|
|
||||||
|
Moodle's LTI module handles the authentication:
|
||||||
|
|
||||||
|
1. **OIDC Initiate Login**: Moodle POSTs to MediaCMS's OIDC login endpoint
|
||||||
|
2. **Authorization Request**: MediaCMS redirects back to Moodle's `/mod/lti/auth.php`
|
||||||
|
3. **JWT Token Generation**: Moodle creates a signed JWT with user info
|
||||||
|
4. **LTI Launch**: Moodle POSTs the JWT to MediaCMS's launch endpoint
|
||||||
|
5. **Content Selection**: MediaCMS displays the authenticated video library
|
||||||
|
|
||||||
|
### Step 5: Video Selection (postMessage)
|
||||||
|
|
||||||
|
When the user selects a video in MediaCMS, it sends a `postMessage` to the parent window:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// MediaCMS sends:
|
||||||
|
window.parent.postMessage({
|
||||||
|
type: 'videoSelected',
|
||||||
|
embedUrl: 'https://mediacms.io/embed/xyz123',
|
||||||
|
videoId: 'xyz123'
|
||||||
|
}, '*');
|
||||||
|
|
||||||
|
// Plugin receives in handleIframeLibraryMessage():
|
||||||
|
if (data.type === 'videoSelected' && data.embedUrl) {
|
||||||
|
this.selectIframeLibraryVideo(root, data.embedUrl, data.videoId);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup Instructions
|
||||||
|
|
||||||
|
### For Administrators
|
||||||
|
|
||||||
|
#### 1. Create External Tool in Moodle
|
||||||
|
|
||||||
|
1. Go to **Site Administration → Plugins → Activity modules → External tool → Manage tools**
|
||||||
|
2. Click **"Configure a tool manually"**
|
||||||
|
3. Fill in the following:
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Tool name | MediaCMS Video Library |
|
||||||
|
| Tool URL | `https://lti.mediacms.io/lti/launch/` |
|
||||||
|
| LTI version | LTI 1.3 |
|
||||||
|
| Public keyset URL | `https://lti.mediacms.io/.well-known/jwks.json` |
|
||||||
|
| Initiate login URL | `https://lti.mediacms.io/lti/oidc/login/` |
|
||||||
|
| Redirection URI(s) | `https://lti.mediacms.io/lti/launch/` |
|
||||||
|
| Content Selection URL | `https://lti.mediacms.io/lti/select-media/` |
|
||||||
|
| Supports Deep Linking | ✓ Yes |
|
||||||
|
|
||||||
|
4. Save and note the **Tool ID** (visible in URL when editing: `id=X`)
|
||||||
|
|
||||||
|
#### 2. Register Moodle with MediaCMS
|
||||||
|
|
||||||
|
Contact MediaCMS administrator with:
|
||||||
|
- **Platform ID (Issuer)**: Your Moodle URL (e.g., `https://moodle.example.com`)
|
||||||
|
- **Client ID**: Generated by Moodle after creating the tool
|
||||||
|
- **Public Keyset URL**: `https://moodle.example.com/mod/lti/certs.php`
|
||||||
|
- **Access Token URL**: `https://moodle.example.com/mod/lti/token.php`
|
||||||
|
- **Authentication Request URL**: `https://moodle.example.com/mod/lti/auth.php`
|
||||||
|
|
||||||
|
#### 3. Configure Plugin Settings
|
||||||
|
|
||||||
|
1. Go to **Site Administration → Plugins → Text editors → TinyMCE → MediaCMS settings**
|
||||||
|
2. Enter the **Tool ID** from step 1
|
||||||
|
3. Save changes
|
||||||
|
|
||||||
|
### For Developers
|
||||||
|
|
||||||
|
#### Adding Debug Logging
|
||||||
|
|
||||||
|
The code includes console.log statements (guarded by eslint-disable):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('loadIframeLibrary called, LTI config:', ltiConfig);
|
||||||
|
```
|
||||||
|
|
||||||
|
Check browser console for:
|
||||||
|
- `loadIframeLibrary called, LTI config: {...}` - Shows if LTI config is received
|
||||||
|
- `Submitting LTI form to: ...` - Shows form submission
|
||||||
|
- `LTI iframe loaded` - Shows iframe load event
|
||||||
|
|
||||||
|
#### Testing Without LTI
|
||||||
|
|
||||||
|
If LTI is not configured (toolId = 0), the plugin falls back to static URL loading:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (ltiConfig?.toolId && ltiConfig?.courseId && ltiConfig?.contentItemUrl) {
|
||||||
|
this.loadIframeLibraryViaLti(root, ltiConfig);
|
||||||
|
} else {
|
||||||
|
this.loadIframeLibraryStatic(root); // Fallback
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rebuilding JavaScript
|
||||||
|
|
||||||
|
After modifying AMD modules, rebuild with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ensure Node.js 22.x is active
|
||||||
|
nvm use 22
|
||||||
|
|
||||||
|
# Run grunt from Moodle root
|
||||||
|
cd /path/to/moodle
|
||||||
|
grunt amd
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Nothing happens when clicking Video Library tab"
|
||||||
|
|
||||||
|
1. **Check LTI Tool ID**: Go to plugin settings and verify Tool ID is set
|
||||||
|
2. **Check browser console**: Look for `loadIframeLibrary` logs
|
||||||
|
3. **Verify External Tool exists**: Ensure the Tool ID corresponds to a valid tool
|
||||||
|
4. **Check course context**: LTI requires a valid course ID (not site level)
|
||||||
|
|
||||||
|
### "LTI authentication fails"
|
||||||
|
|
||||||
|
1. **Verify MediaCMS registration**: Moodle must be registered as a platform in MediaCMS
|
||||||
|
2. **Check URLs**: Ensure all LTI URLs are correct and accessible
|
||||||
|
3. **Check HTTPS**: LTI 1.3 requires HTTPS on both ends
|
||||||
|
4. **Review Moodle logs**: Check `Site Administration → Reports → Logs` for LTI errors
|
||||||
|
|
||||||
|
### "Video selection doesn't work"
|
||||||
|
|
||||||
|
1. **Check postMessage handling**: Verify MediaCMS sends expected message format
|
||||||
|
2. **Check browser console**: Look for `handleIframeLibraryMessage` logs
|
||||||
|
3. **Verify origin**: Some browsers block cross-origin postMessages
|
||||||
|
|
||||||
|
## Message Formats Supported
|
||||||
|
|
||||||
|
The plugin handles multiple postMessage formats:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Custom format
|
||||||
|
{ type: 'videoSelected', embedUrl: '...', videoId: '...' }
|
||||||
|
|
||||||
|
// LTI Deep Linking format
|
||||||
|
{ type: 'ltiDeepLinkingResponse', content_items: [...] }
|
||||||
|
|
||||||
|
// MediaCMS specific
|
||||||
|
{ action: 'selectMedia', embedUrl: '...', mediaId: '...' }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **LTI 1.3 Security**: All authentication uses signed JWTs and OIDC
|
||||||
|
2. **No credentials in plugin**: The plugin never handles user credentials
|
||||||
|
3. **Moodle handles auth**: All sensitive operations go through Moodle's LTI module
|
||||||
|
4. **postMessage validation**: Consider adding origin checks for production
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [LTI 1.3 Specification](https://www.imsglobal.org/spec/lti/v1p3)
|
||||||
|
- [Moodle LTI Documentation](https://docs.moodle.org/en/LTI_and_Moodle)
|
||||||
|
- [Moodle External Tool](https://docs.moodle.org/en/External_tool)
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
When modifying LTI-related code:
|
||||||
|
|
||||||
|
1. Test with a real LTI tool (not just mocks)
|
||||||
|
2. Test in multiple browsers (postMessage behavior varies)
|
||||||
|
3. Test both with and without LTI configured (fallback path)
|
||||||
|
4. Update this documentation if behavior changes
|
||||||
15
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js
vendored
Executable file
15
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js
vendored
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
define("tiny_mediacms/autoconvert",["exports","./options"],(function(_exports,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setupAutoConvert=_exports.isMediaCMSUrl=_exports.convertToEmbed=void 0;
|
||||||
|
/**
|
||||||
|
* Tiny MediaCMS Auto-convert module.
|
||||||
|
*
|
||||||
|
* This module automatically converts pasted MediaCMS URLs into embedded videos.
|
||||||
|
* When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl),
|
||||||
|
* it will be automatically converted to an iframe embed.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/autoconvert
|
||||||
|
* @copyright 2024
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
const MEDIACMS_VIEW_URL_PATTERN=/^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/,parseMediaCMSUrl=text=>{if(!text||"string"!=typeof text)return null;const trimmed=text.trim(),match=trimmed.match(MEDIACMS_VIEW_URL_PATTERN);return match?{baseUrl:match[1],videoId:match[2],originalUrl:trimmed}:null},isDomainAllowed=(parsed,config)=>{const configuredBaseUrl=config.autoConvertBaseUrl||config.mediacmsBaseUrl;if(!configuredBaseUrl)return!0;try{const configuredUrl=new URL(configuredBaseUrl),pastedUrl=new URL(parsed.baseUrl);return configuredUrl.host===pastedUrl.host}catch(e){return!0}},generateEmbedHtml=function(parsed){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const embedUrl=new URL("".concat(parsed.baseUrl,"/embed"));embedUrl.searchParams.set("m",parsed.videoId),embedUrl.searchParams.set("showTitle",!1!==options.showTitle?"1":"0"),embedUrl.searchParams.set("showRelated",!1!==options.showRelated?"1":"0"),embedUrl.searchParams.set("showUserAvatar",!1!==options.showUserAvatar?"1":"0"),embedUrl.searchParams.set("linkTitle",!1!==options.linkTitle?"1":"0");const html='<iframe width="400" height="300" style="display: block; border: 0;" '+'src="'.concat(embedUrl.toString(),'" ')+'allowfullscreen="allowfullscreen"></iframe>';return html};_exports.setupAutoConvert=editor=>{const config=(0,_options.getData)(editor)||{};!1!==config.autoConvertEnabled&&(editor.on("paste",(e=>{handlePasteEvent(editor,e,config)})),editor.on("input",(e=>{handleInputEvent(editor,e,config)})))};const handlePasteEvent=(editor,e,config)=>{const clipboardData=e.clipboardData||window.clipboardData;if(!clipboardData)return;const text=clipboardData.getData("text/plain")||clipboardData.getData("text");if(!text)return;const parsed=parseMediaCMSUrl(text);if(!parsed)return;if(!isDomainAllowed(parsed,config))return;e.preventDefault(),e.stopPropagation();const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{editor.insertContent(embedHtml),editor.selection.collapse(!1)}),0)},handleInputEvent=(editor,e,config)=>{if("insertFromPaste"!==e.inputType&&"insertText"!==e.inputType)return;const node=editor.selection.getNode();if(!node||"P"!==node.nodeName)return;const text=node.textContent||"",parsed=parseMediaCMSUrl(text);if(!parsed||!isDomainAllowed(parsed,config))return;const trimmedHtml=node.innerHTML.trim();if(trimmedHtml!==text.trim()&&!trimmedHtml.startsWith(text.trim()))return;const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{const currentText=node.textContent||"",currentParsed=parseMediaCMSUrl(currentText);currentParsed&¤tParsed.originalUrl===parsed.originalUrl&&(editor.selection.select(node),editor.insertContent(embedHtml))}),100)};_exports.isMediaCMSUrl=text=>null!==parseMediaCMSUrl(text);_exports.convertToEmbed=function(url){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const parsed=parseMediaCMSUrl(url);return parsed?generateEmbedHtml(parsed,options):null}}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=autoconvert.min.js.map
|
||||||
File diff suppressed because one or more lines are too long
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js.map
Executable file
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
define("tiny_mediacms/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={pluginName:"tiny_mediacms/plugin",component:"tiny_mediacms",iframeButtonName:"tiny_mediacms_iframe",iframeMenuItemName:"tiny_mediacms_iframe",iframeIcon:"tiny_mediacms_iframe"},_exports.default}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=common.min.js.map
|
||||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js.map
Executable file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"common.min.js","sources":["../src/common.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media common values.\n *\n * @module tiny_mediacms/common\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n pluginName: 'tiny_mediacms/plugin',\n component: 'tiny_mediacms',\n iframeButtonName: 'tiny_mediacms_iframe',\n iframeMenuItemName: 'tiny_mediacms_iframe',\n iframeIcon: 'tiny_mediacms_iframe',\n};\n"],"names":["pluginName","component","iframeButtonName","iframeMenuItemName","iframeIcon"],"mappings":"sKAuBe,CACXA,WAAY,uBACZC,UAAW,gBACXC,iBAAkB,uBAClBC,mBAAoB,uBACpBC,WAAY"}
|
||||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
define("tiny_mediacms/configuration",["exports","./common","editor_tiny/utils"],(function(_exports,_common,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;_exports.configure=instanceConfig=>{return{contextmenu:(0,_utils.addContextmenuItem)(instanceConfig.contextmenu,_common.iframeButtonName),menu:(menu=instanceConfig.menu,menu.insert.items="".concat(_common.iframeMenuItemName," ").concat(menu.insert.items),menu),toolbar:(toolbar=instanceConfig.toolbar,toolbar.map((section=>("content"===section.name&§ion.items.unshift(_common.iframeButtonName),section))))};var toolbar,menu}}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=configuration.min.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"configuration.min.js","sources":["../src/configuration.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media configuration.\n *\n * @module tiny_mediacms/configuration\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {\n iframeButtonName,\n iframeMenuItemName,\n} from './common';\nimport {\n addContextmenuItem,\n} from 'editor_tiny/utils';\n\nconst configureMenu = (menu) => {\n // Add the Iframe Embed to the insert menu.\n menu.insert.items = `${iframeMenuItemName} ${menu.insert.items}`;\n\n return menu;\n};\n\nconst configureToolbar = (toolbar) => {\n // The toolbar contains an array of named sections.\n // The Moodle integration ensures that there is a section called 'content'.\n\n return toolbar.map((section) => {\n if (section.name === 'content') {\n // Insert the iframe button at the start of it.\n section.items.unshift(iframeButtonName);\n }\n\n return section;\n });\n};\n\nexport const configure = (instanceConfig) => {\n // Update the instance configuration to add the Iframe Embed menu option to the menus and toolbars.\n return {\n contextmenu: addContextmenuItem(instanceConfig.contextmenu, iframeButtonName),\n menu: configureMenu(instanceConfig.menu),\n toolbar: configureToolbar(instanceConfig.toolbar),\n };\n};\n"],"names":["instanceConfig","contextmenu","iframeButtonName","menu","insert","items","iframeMenuItemName","toolbar","map","section","name","unshift"],"mappings":"wNAoD0BA,uBAEf,CACHC,aAAa,6BAAmBD,eAAeC,YAAaC,0BAC5DC,MAzBeA,KAyBKH,eAAeG,KAvBvCA,KAAKC,OAAOC,gBAAWC,uCAAsBH,KAAKC,OAAOC,OAElDF,MAsBHI,SAnBkBA,QAmBQP,eAAeO,QAftCA,QAAQC,KAAKC,UACK,YAAjBA,QAAQC,MAERD,QAAQJ,MAAMM,QAAQT,0BAGnBO,aAVWF,IAAAA,QAPHJ"}
|
||||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js.map
Executable file
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
define("tiny_mediacms/embedmodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class EmbedModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=EmbedModal,_defineProperty(EmbedModal,"TYPE","".concat(_common.component,"/modal")),_defineProperty(EmbedModal,"TEMPLATE","".concat(_common.component,"/embed_media_modal")),_exports.default}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=embedmodal.min.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"embedmodal.min.js","sources":["../src/embedmodal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Embedded Media Management Modal for Tiny.\n *\n * @module tiny_mediacms/embedmodal\n * @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class EmbedModal extends Modal {\n static TYPE = `${component}/modal`;\n static TEMPLATE = `${component}/embed_media_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["EmbedModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,6CADAV,gCAEIU"}
|
||||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
define("tiny_mediacms/iframemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class IframeModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=IframeModal,_defineProperty(IframeModal,"TYPE","".concat(_common.component,"/iframemodal")),_defineProperty(IframeModal,"TEMPLATE","".concat(_common.component,"/iframe_embed_modal")),_exports.default}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=iframemodal.min.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"iframemodal.min.js","sources":["../src/iframemodal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Iframe Embed Modal for Tiny Media2.\n *\n * @module tiny_mediacms/iframemodal\n * @copyright 2024\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class IframeModal extends Modal {\n static TYPE = `${component}/iframemodal`;\n static TEMPLATE = `${component}/iframe_embed_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["IframeModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"kaA0BqBA,oBAAoBC,eAIrCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,kEAlBHN,6BACAU,mDADAV,iCAEIU"}
|
||||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js.map
Executable file
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
define("tiny_mediacms/imagehelpers",["exports","core/templates"],(function(_exports,_templates){var obj;
|
||||||
|
/**
|
||||||
|
* Tiny media plugin image helpers.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/imagehelpers
|
||||||
|
* @copyright 2024 Meirza <meirza.arson@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showElements=_exports.isPercentageValue=_exports.hideElements=_exports.footerImageInsert=_exports.footerImageDetails=_exports.bodyImageInsert=_exports.bodyImageDetails=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};_exports.bodyImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert",{...templateContext}).then((_ref=>{let{html:html,js:js}=_ref;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert_footer",{...templateContext}).then((_ref2=>{let{html:html,js:js}=_ref2;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.bodyImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details",{...templateContext}).then((_ref3=>{let{html:html,js:js}=_ref3;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details_footer",{...templateContext}).then((_ref4=>{let{html:html,js:js}=_ref4;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.showElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.remove("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.remove("d-none")}};_exports.hideElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.add("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.add("d-none")}};_exports.isPercentageValue=value=>value.match(/\d+%/)}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=imagehelpers.min.js.map
|
||||||
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
define("tiny_mediacms/imagemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class ImageModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=ImageModal,_defineProperty(ImageModal,"TYPE","".concat(_common.component,"/imagemodal")),_defineProperty(ImageModal,"TEMPLATE","".concat(_common.component,"/insert_image_modal")),ImageModal.registerModalType(),_exports.default}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=imagemodal.min.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"imagemodal.min.js","sources":["../src/imagemodal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Image Modal for Tiny.\n *\n * @module tiny_mediacms/imagemodal\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class ImageModal extends Modal {\n static TYPE = `${component}/imagemodal`;\n static TEMPLATE = `${component}/insert_image_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n\nImageModal.registerModalType();\n"],"names":["ImageModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component","registerModalType"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,kDADAV,gCAEIU,0CAoBzBV,WAAWW"}
|
||||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
define("tiny_mediacms/manager",["exports","core/templates","core/str","core/modal","core/modal_events","./options","core/config"],(function(_exports,_templates,_str,_modal,ModalEvents,_options,_config){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_modal=_interopRequireDefault(_modal),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_config=_interopRequireDefault(_config);return _exports.default=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"area",null),this.editor=editor;const data=(0,_options.getData)(editor);this.area=data.params.area,this.area.itemid=data.fpoptions.image.itemid}async displayDialogue(){const modal=await _modal.default.create({large:!0,title:(0,_str.getString)("mediamanagerproperties","tiny_mediacms"),body:_templates.default.render("tiny_mediacms/mm2_iframe",{src:this.getIframeURL()}),removeOnClose:!0,show:!0});return modal.getRoot().on(ModalEvents.bodyRendered,(()=>{this.selectFirstElement()})),document.querySelector(".modal-lg").style.cssText="max-width: 850px",modal}selectFirstElement(){const iframe=document.getElementById("mm2-iframe");iframe.addEventListener("load",(function(){let intervalId=setInterval((function(){const iDocument=iframe.contentWindow.document;if(iDocument.querySelector(".filemanager")){const firstFocusableElement=iDocument.querySelector(".fp-navbar a:not([disabled])");firstFocusableElement&&firstFocusableElement.focus(),clearInterval(intervalId)}}),200)}))}getIframeURL(){const url=new URL("".concat(_config.default.wwwroot,"/lib/editor/tiny/plugins/mediacms/manage.php"));url.searchParams.append("elementid",this.editor.getElement().id);for(const key in this.area)url.searchParams.append(key,this.area[key]);return url.toString()}},_exports.default}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=manager.min.js.map
|
||||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js.map
Executable file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"manager.min.js","sources":["../src/manager.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media Manager plugin class for Moodle.\n *\n * @module tiny_mediacms/manager\n * @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport Modal from 'core/modal';\nimport * as ModalEvents from 'core/modal_events';\nimport {getData} from './options';\nimport Config from 'core/config';\n\nexport default class MediaManager {\n\n editor = null;\n area = null;\n\n constructor(editor) {\n this.editor = editor;\n const data = getData(editor);\n this.area = data.params.area;\n this.area.itemid = data.fpoptions.image.itemid;\n }\n\n async displayDialogue() {\n const modal = await Modal.create({\n large: true,\n title: getString('mediamanagerproperties', 'tiny_mediacms'),\n body: Templates.render('tiny_mediacms/mm2_iframe', {\n src: this.getIframeURL()\n }),\n removeOnClose: true,\n show: true,\n });\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n this.selectFirstElement();\n });\n\n document.querySelector('.modal-lg').style.cssText = `max-width: 850px`;\n return modal;\n }\n\n // It will select the first element in the file manager.\n selectFirstElement() {\n const iframe = document.getElementById('mm2-iframe');\n iframe.addEventListener('load', function() {\n let intervalId = setInterval(function() {\n const iDocument = iframe.contentWindow.document;\n if (iDocument.querySelector('.filemanager')) {\n const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])');\n if (firstFocusableElement) {\n firstFocusableElement.focus();\n }\n clearInterval(intervalId);\n }\n }, 200);\n });\n }\n\n getIframeURL() {\n const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/mediacms/manage.php`);\n url.searchParams.append('elementid', this.editor.getElement().id);\n for (const key in this.area) {\n url.searchParams.append(key, this.area[key]);\n }\n return url.toString();\n }\n}\n"],"names":["constructor","editor","data","area","params","itemid","fpoptions","image","modal","Modal","create","large","title","body","Templates","render","src","this","getIframeURL","removeOnClose","show","getRoot","on","ModalEvents","bodyRendered","selectFirstElement","document","querySelector","style","cssText","iframe","getElementById","addEventListener","intervalId","setInterval","iDocument","contentWindow","firstFocusableElement","focus","clearInterval","url","URL","Config","wwwroot","searchParams","append","getElement","id","key","toString"],"mappings":"mmDAmCIA,YAAYC,sCAHH,kCACF,WAGEA,OAASA,aACRC,MAAO,oBAAQD,aAChBE,KAAOD,KAAKE,OAAOD,UACnBA,KAAKE,OAASH,KAAKI,UAAUC,MAAMF,qCAIlCG,YAAcC,eAAMC,OAAO,CAC7BC,OAAO,EACPC,OAAO,kBAAU,yBAA0B,iBAC3CC,KAAMC,mBAAUC,OAAO,2BAA4B,CAC/CC,IAAKC,KAAKC,iBAEdC,eAAe,EACfC,MAAM,WAEVZ,MAAMa,UAAUC,GAAGC,YAAYC,cAAc,UACpCC,wBAGTC,SAASC,cAAc,aAAaC,MAAMC,2BACnCrB,MAIXiB,2BACUK,OAASJ,SAASK,eAAe,cACvCD,OAAOE,iBAAiB,QAAQ,eACxBC,WAAaC,aAAY,iBACnBC,UAAYL,OAAOM,cAAcV,YACnCS,UAAUR,cAAc,gBAAiB,OACnCU,sBAAwBF,UAAUR,cAAc,gCAClDU,uBACAA,sBAAsBC,QAE1BC,cAAcN,eAEnB,QAIXf,qBACUsB,IAAM,IAAIC,cAAOC,gBAAOC,yDAC9BH,IAAII,aAAaC,OAAO,YAAa5B,KAAKhB,OAAO6C,aAAaC,QACzD,MAAMC,OAAO/B,KAAKd,KACnBqC,IAAII,aAAaC,OAAOG,IAAK/B,KAAKd,KAAK6C,aAEpCR,IAAIS"}
|
||||||
11
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js
vendored
Executable file
11
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js
vendored
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
define("tiny_mediacms/options",["exports","editor_tiny/options","./common"],(function(_exports,_options,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.getPermissions=_exports.getLti=_exports.getImagePermissions=_exports.getEmbedPermissions=_exports.getData=void 0;
|
||||||
|
/**
|
||||||
|
* Options helper for Tiny Media plugin.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/options
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
const dataName=(0,_options.getPluginOptionName)(_common.pluginName,"data"),permissionsName=(0,_options.getPluginOptionName)(_common.pluginName,"permissions"),ltiName=(0,_options.getPluginOptionName)(_common.pluginName,"lti");_exports.register=editor=>{const registerOption=editor.options.register;registerOption(permissionsName,{processor:"object",default:{image:{filepicker:!1}}}),registerOption(dataName,{processor:"object",default:{mediacmsApiUrl:"",mediacmsBaseUrl:"",mediacmsPageSize:12,autoConvertEnabled:!0,autoConvertBaseUrl:"",autoConvertOptions:{showTitle:!0,linkTitle:!0,showRelated:!0,showUserAvatar:!0}}}),registerOption(ltiName,{processor:"object",default:{toolId:0,courseId:0,contentItemUrl:""}})};const getPermissions=editor=>editor.options.get(permissionsName);_exports.getPermissions=getPermissions;_exports.getImagePermissions=editor=>getPermissions(editor).image;_exports.getEmbedPermissions=editor=>getPermissions(editor).embed;_exports.getData=editor=>editor.options.get(dataName);_exports.getLti=editor=>editor.options.get(ltiName)}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=options.min.js.map
|
||||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js.map
Executable file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"options.min.js","sources":["../src/options.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Options helper for Tiny Media plugin.\n *\n * @module tiny_mediacms/options\n * @copyright 2022 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getPluginOptionName} from 'editor_tiny/options';\nimport {pluginName} from './common';\n\nconst dataName = getPluginOptionName(pluginName, 'data');\nconst permissionsName = getPluginOptionName(pluginName, 'permissions');\nconst ltiName = getPluginOptionName(pluginName, 'lti');\n\n/**\n * Register the options for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n */\nexport const register = (editor) => {\n const registerOption = editor.options.register;\n\n registerOption(permissionsName, {\n processor: 'object',\n \"default\": {\n image: {\n filepicker: false,\n }\n },\n });\n\n registerOption(dataName, {\n processor: 'object',\n \"default\": {\n // MediaCMS video library configuration\n mediacmsApiUrl: '', // e.g., 'https://deic.mediacms.io/api/v1/media'\n mediacmsBaseUrl: '', // e.g., 'https://deic.mediacms.io'\n mediacmsPageSize: 12,\n // Auto-conversion settings\n autoConvertEnabled: true, // Enable/disable auto-conversion of pasted MediaCMS URLs\n autoConvertBaseUrl: '', // Base URL to restrict auto-conversion (empty = allow all MediaCMS domains)\n autoConvertOptions: {\n // Default embed options for auto-converted videos\n showTitle: true,\n linkTitle: true,\n showRelated: true,\n showUserAvatar: true,\n },\n },\n });\n\n registerOption(ltiName, {\n processor: 'object',\n \"default\": {\n // LTI configuration for MediaCMS iframe library\n toolId: 0, // LTI external tool ID\n courseId: 0, // Current course ID\n contentItemUrl: '', // URL to /mod/lti/contentitem.php for Deep Linking\n },\n });\n};\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getPermissions = (editor) => editor.options.get(permissionsName);\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getImagePermissions = (editor) => getPermissions(editor).image;\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getEmbedPermissions = (editor) => getPermissions(editor).embed;\n\n/**\n * Get the data configuration for the Media Manager.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getData = (editor) => editor.options.get(dataName);\n\n/**\n * Get the LTI configuration for the MediaCMS iframe library.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getLti = (editor) => editor.options.get(ltiName);\n"],"names":["dataName","pluginName","permissionsName","ltiName","editor","registerOption","options","register","processor","image","filepicker","mediacmsApiUrl","mediacmsBaseUrl","mediacmsPageSize","autoConvertEnabled","autoConvertBaseUrl","autoConvertOptions","showTitle","linkTitle","showRelated","showUserAvatar","toolId","courseId","contentItemUrl","getPermissions","get","embed"],"mappings":";;;;;;;;MA0BMA,UAAW,gCAAoBC,mBAAY,QAC3CC,iBAAkB,gCAAoBD,mBAAY,eAClDE,SAAU,gCAAoBF,mBAAY,yBAOvBG,eACfC,eAAiBD,OAAOE,QAAQC,SAEtCF,eAAeH,gBAAiB,CAC5BM,UAAW,iBACA,CACPC,MAAO,CACHC,YAAY,MAKxBL,eAAeL,SAAU,CACrBQ,UAAW,iBACA,CAEPG,eAAgB,GAChBC,gBAAiB,GACjBC,iBAAkB,GAElBC,oBAAoB,EACpBC,mBAAoB,GACpBC,mBAAoB,CAEhBC,WAAW,EACXC,WAAW,EACXC,aAAa,EACbC,gBAAgB,MAK5Bf,eAAeF,QAAS,CACpBK,UAAW,iBACA,CAEPa,OAAQ,EACRC,SAAU,EACVC,eAAgB,aAWfC,eAAkBpB,QAAWA,OAAOE,QAAQmB,IAAIvB,qFAQzBE,QAAWoB,eAAepB,QAAQK,mCAQlCL,QAAWoB,eAAepB,QAAQsB,uBAQ9CtB,QAAWA,OAAOE,QAAQmB,IAAIzB,0BAQ/BI,QAAWA,OAAOE,QAAQmB,IAAItB"}
|
||||||
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
define("tiny_mediacms/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./common","./commands","./configuration","./options","./autoconvert"],(function(_exports,_loader,_utils,_common,Commands,Configuration,Options,_autoconvert){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
|
||||||
|
/**
|
||||||
|
* Tiny Media plugin for Moodle.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/plugin
|
||||||
|
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Commands=_interopRequireWildcard(Commands),Configuration=_interopRequireWildcard(Configuration),Options=_interopRequireWildcard(Options);var _default=new Promise((async resolve=>{const[tinyMCE,setupCommands,pluginMetadata]=await Promise.all([(0,_loader.getTinyMCE)(),Commands.getSetup(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName)]);tinyMCE.PluginManager.add("".concat(_common.component,"/plugin"),(editor=>(Options.register(editor),setupCommands(editor),(0,_autoconvert.setupAutoConvert)(editor),editor.on("GetContent",(e=>{if("html"===e.format){const tempDiv=document.createElement("div");tempDiv.innerHTML=e.content,tempDiv.querySelectorAll(".tiny-mediacms-edit-btn").forEach((btn=>btn.remove())),tempDiv.querySelectorAll(".tiny-mediacms-iframe-wrapper").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),tempDiv.querySelectorAll(".tiny-iframe-responsive").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),e.content=tempDiv.innerHTML}})),pluginMetadata))),resolve(["".concat(_common.component,"/plugin"),Configuration])}));return _exports.default=_default,_exports.default}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=plugin.min.js.map
|
||||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js.map
Executable file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"plugin.min.js","sources":["../src/plugin.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Media plugin for Moodle.\n *\n * @module tiny_mediacms/plugin\n * @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {getTinyMCE} from 'editor_tiny/loader';\nimport {getPluginMetadata} from 'editor_tiny/utils';\n\nimport {component, pluginName} from './common';\nimport * as Commands from './commands';\nimport * as Configuration from './configuration';\nimport * as Options from './options';\nimport {setupAutoConvert} from './autoconvert';\n\n// eslint-disable-next-line no-async-promise-executor\nexport default new Promise(async(resolve) => {\n const [\n tinyMCE,\n setupCommands,\n pluginMetadata,\n ] = await Promise.all([\n getTinyMCE(),\n Commands.getSetup(),\n getPluginMetadata(component, pluginName),\n ]);\n\n tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {\n // Register options.\n Options.register(editor);\n\n // Setup the Commands (buttons, menu items, and so on).\n setupCommands(editor);\n\n // Setup auto-conversion of pasted MediaCMS URLs.\n setupAutoConvert(editor);\n\n // Clean up editor-only elements before content is saved.\n // Remove wrapper divs and edit buttons that are only for the editor UI.\n editor.on('GetContent', (e) => {\n if (e.format === 'html') {\n // Create a temporary container to manipulate the HTML\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = e.content;\n\n // Remove edit buttons\n tempDiv.querySelectorAll('.tiny-mediacms-edit-btn').forEach(btn => btn.remove());\n\n // Unwrap iframes from tiny-mediacms-iframe-wrapper\n tempDiv.querySelectorAll('.tiny-mediacms-iframe-wrapper').forEach(wrapper => {\n const iframe = wrapper.querySelector('iframe');\n if (iframe) {\n wrapper.parentNode.insertBefore(iframe, wrapper);\n }\n wrapper.remove();\n });\n\n // Unwrap iframes from tiny-iframe-responsive\n tempDiv.querySelectorAll('.tiny-iframe-responsive').forEach(wrapper => {\n const iframe = wrapper.querySelector('iframe');\n if (iframe) {\n wrapper.parentNode.insertBefore(iframe, wrapper);\n }\n wrapper.remove();\n });\n\n e.content = tempDiv.innerHTML;\n }\n });\n\n return pluginMetadata;\n });\n\n // Resolve the Media Plugin and include configuration.\n resolve([`${component}/plugin`, Configuration]);\n});\n"],"names":["Promise","async","tinyMCE","setupCommands","pluginMetadata","all","Commands","getSetup","component","pluginName","PluginManager","add","editor","Options","register","on","e","format","tempDiv","document","createElement","innerHTML","content","querySelectorAll","forEach","btn","remove","wrapper","iframe","querySelector","parentNode","insertBefore","resolve","Configuration"],"mappings":";;;;;;;2OAgCe,IAAIA,SAAQC,MAAAA,gBAEnBC,QACAC,cACAC,sBACMJ,QAAQK,IAAI,EAClB,wBACAC,SAASC,YACT,4BAAkBC,kBAAWC,sBAGjCP,QAAQQ,cAAcC,cAAOH,8BAAqBI,SAE9CC,QAAQC,SAASF,QAGjBT,cAAcS,0CAGGA,QAIjBA,OAAOG,GAAG,cAAeC,OACJ,SAAbA,EAAEC,OAAmB,OAEfC,QAAUC,SAASC,cAAc,OACvCF,QAAQG,UAAYL,EAAEM,QAGtBJ,QAAQK,iBAAiB,2BAA2BC,SAAQC,KAAOA,IAAIC,WAGvER,QAAQK,iBAAiB,iCAAiCC,SAAQG,gBACxDC,OAASD,QAAQE,cAAc,UACjCD,QACAD,QAAQG,WAAWC,aAAaH,OAAQD,SAE5CA,QAAQD,YAIZR,QAAQK,iBAAiB,2BAA2BC,SAAQG,gBAClDC,OAASD,QAAQE,cAAc,UACjCD,QACAD,QAAQG,WAAWC,aAAaH,OAAQD,SAE5CA,QAAQD,YAGZV,EAAEM,QAAUJ,QAAQG,cAIrBjB,kBAIX4B,QAAQ,WAAIxB,6BAAoByB"}
|
||||||
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js
vendored
Executable file
3
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
define("tiny_mediacms/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_imagecms_urlentrysubmit",imageBrowser:".openimagecmsbrowser",addUrl:".tiny_imagecms_addurl",deleteImage:".tiny_imagecms_deleteicon"},elements:{form:"form.tiny_imagecms_form",alignSettings:".tiny_imagecms_button",alt:".tiny_imagecms_altentry",altWarning:".tiny_imagecms_altwarning",height:".tiny_imagecms_heightentry",width:".tiny_imagecms_widthentry",url:".tiny_imagecms_urlentry",urlWarning:".tiny_imagecms_urlwarning",size:".tiny_imagecms_size",presentation:".tiny_imagecms_presentation",constrain:".tiny_imagecms_constrain",customStyle:".tiny_imagecms_customstyle",preview:".tiny_imagecms_preview",previewBox:".tiny_imagecms_preview_box",loaderIcon:".tiny_imagecms_loader",loaderIconContainer:".tiny_imagecms_loader_container",insertImage:".tiny_imagecms_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_imagecms_dropzone_container",fileInput:"#tiny_imagecms_fileinput",fileNameLabel:".tiny_imagecms_filename",sizeOriginal:".tiny_imagecms_sizeoriginal",sizeCustom:".tiny_imagecms_sizecustom",properties:".tiny_imagecms_properties"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_mediacms_submit",mediaBrowser:".openmediacmsbrowser"},elements:{form:"form.tiny_mediacms_form",source:".tiny_mediacms_source",track:".tiny_mediacms_track",mediaSource:".tiny_mediacms_media_source",linkSource:".tiny_mediacms_link_source",linkSize:".tiny_mediacms_link_size",posterSource:".tiny_mediacms_poster_source",posterSize:".tiny_mediacms_poster_size",displayOptions:".tiny_mediacms_display_options",name:".tiny_mediacms_name_entry",title:".tiny_mediacms_title_entry",url:".tiny_mediacms_url_entry",width:".tiny_mediacms_width_entry",height:".tiny_mediacms_height_entry",trackSource:".tiny_mediacms_track_source",trackKind:".tiny_mediacms_track_kind_entry",trackLabel:".tiny_mediacms_track_label_entry",trackLang:".tiny_mediacms_track_lang_entry",trackDefault:".tiny_mediacms_track_default",mediaControl:".tiny_mediacms_controls",mediaAutoplay:".tiny_mediacms_autoplay",mediaMute:".tiny_mediacms_mute",mediaLoop:".tiny_mediacms_loop",advancedSettings:".tiny_mediacms_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}},IFRAME:{actions:{remove:'[data-action="remove"]'},elements:{form:"form.tiny_iframecms_form",url:".tiny_iframecms_url",urlWarning:".tiny_iframecms_url_warning",showTitle:".tiny_iframecms_showtitle",linkTitle:".tiny_iframecms_linktitle",showRelated:".tiny_iframecms_showrelated",showUserAvatar:".tiny_iframecms_showuseravatar",responsive:".tiny_iframecms_responsive",startAt:".tiny_iframecms_startat",startAtEnabled:".tiny_iframecms_startat_enabled",aspectRatio:".tiny_iframecms_aspectratio",width:".tiny_iframecms_width",height:".tiny_iframecms_height",preview:".tiny_iframecms_preview",previewContainer:".tiny_iframecms_preview_container",tabs:".tiny_iframecms_tabs",tabUrlBtn:".tiny_iframecms_tab_url_btn",tabIframeLibraryBtn:".tiny_iframecms_tab_iframe_library_btn",paneUrl:".tiny_iframecms_pane_url",paneIframeLibrary:".tiny_iframecms_pane_iframe_library",iframeLibraryContainer:".tiny_iframecms_iframe_library_container",iframeLibraryPlaceholder:".tiny_iframecms_iframe_library_placeholder",iframeLibraryLoading:".tiny_iframecms_iframe_library_loading",iframeLibraryFrame:".tiny_iframecms_iframe_library_frame"},aspectRatios:{"16:9":{width:560,height:315},"4:3":{width:560,height:420},"1:1":{width:400,height:400},custom:null}}},_exports.default}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=selectors.min.js.map
|
||||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js.map
Executable file
File diff suppressed because one or more lines are too long
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js
vendored
Executable file
10
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
define("tiny_mediacms/usedfiles",["exports","core/templates","core/config"],(function(_exports,Templates,_config){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,Templates=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}
|
||||||
|
/**
|
||||||
|
* Tiny Media Manager usedfiles.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/usedfiles
|
||||||
|
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/(Templates),_config=(obj=_config)&&obj.__esModule?obj:{default:obj};class UsedFileManager{constructor(files,userContext,itemId,elementId){this.files=files,this.userContext=userContext,this.itemId=itemId,this.elementId=elementId}getElementId(){return this.elementId}getUsedFiles(){const editor=window.parent.tinymce.EditorManager.get(this.getElementId());if(!editor)return window.console.error("Editor not found for ".concat(this.getElementId())),[];const content=editor.getContent(),baseUrl="".concat(_config.default.wwwroot,"/draftfile.php/").concat(this.userContext,"/user/draft/").concat(this.itemId,"/"),pattern=new RegExp("[\"']"+baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")+"(?<filename>.+?)[\\?\"']","gm");return[...content.matchAll(pattern)].map((match=>decodeURIComponent(match.groups.filename)))}findUnusedFiles(usedFiles){return Object.entries(this.files).filter((_ref=>{let[filename]=_ref;return!usedFiles.includes(filename)})).map((_ref2=>{let[filename]=_ref2;return filename}))}findMissingFiles(usedFiles){return usedFiles.filter((filename=>!this.files.hasOwnProperty(filename)))}updateFiles(){const form=document.querySelector("form"),usedFiles=this.getUsedFiles(),unusedFiles=this.findUnusedFiles(usedFiles),missingFiles=this.findMissingFiles(usedFiles);return form.querySelectorAll('input[type=checkbox][name^="deletefile"]').forEach((checkbox=>{unusedFiles.includes(checkbox.dataset.filename)||checkbox.closest(".fitem").remove()})),form.classList.toggle("has-missing-files",!!missingFiles.length),form.classList.toggle("has-unused-files",!!unusedFiles.length),Templates.renderForPromise("tiny_mediacms/missingfiles",{missingFiles:missingFiles}).then((_ref3=>{let{html:html,js:js}=_ref3;Templates.replaceNodeContents(form.querySelector(".missing-files"),html,js)}))}}_exports.init=(files,usercontext,itemid,elementid)=>{const manager=new UsedFileManager(files,usercontext,itemid,elementid);return manager.updateFiles(),manager}}));
|
||||||
|
|
||||||
|
//# sourceMappingURL=usedfiles.min.js.map
|
||||||
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js.map
Executable file
1
lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js.map
Executable file
File diff suppressed because one or more lines are too long
265
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/autoconvert.js
Executable file
265
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/autoconvert.js
Executable file
@@ -0,0 +1,265 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny MediaCMS Auto-convert module.
|
||||||
|
*
|
||||||
|
* This module automatically converts pasted MediaCMS URLs into embedded videos.
|
||||||
|
* When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl),
|
||||||
|
* it will be automatically converted to an iframe embed.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/autoconvert
|
||||||
|
* @copyright 2024
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {getData} from './options';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular expression patterns for MediaCMS URLs.
|
||||||
|
* Matches URLs like:
|
||||||
|
* - https://deic.mediacms.io/view?m=JpBd1Zvdl
|
||||||
|
* - https://example.mediacms.io/view?m=VIDEO_ID
|
||||||
|
* - Custom domains configured in the plugin
|
||||||
|
*/
|
||||||
|
const MEDIACMS_VIEW_URL_PATTERN = /^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string is a valid MediaCMS view URL.
|
||||||
|
*
|
||||||
|
* @param {string} text - The text to check
|
||||||
|
* @returns {Object|null} - Parsed URL info or null if not a valid MediaCMS URL
|
||||||
|
*/
|
||||||
|
const parseMediaCMSUrl = (text) => {
|
||||||
|
if (!text || typeof text !== 'string') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmed = text.trim();
|
||||||
|
|
||||||
|
// Check for MediaCMS view URL pattern
|
||||||
|
const match = trimmed.match(MEDIACMS_VIEW_URL_PATTERN);
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
baseUrl: match[1],
|
||||||
|
videoId: match[2],
|
||||||
|
originalUrl: trimmed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the pasted URL's domain is allowed based on configuration.
|
||||||
|
*
|
||||||
|
* @param {Object} parsed - Parsed URL info
|
||||||
|
* @param {Object} config - Plugin configuration
|
||||||
|
* @returns {boolean} - True if the domain is allowed
|
||||||
|
*/
|
||||||
|
const isDomainAllowed = (parsed, config) => {
|
||||||
|
// If no specific base URL is configured, allow all MediaCMS domains
|
||||||
|
const configuredBaseUrl = config.autoConvertBaseUrl || config.mediacmsBaseUrl;
|
||||||
|
if (!configuredBaseUrl) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the URL's base matches the configured base URL
|
||||||
|
try {
|
||||||
|
const configuredUrl = new URL(configuredBaseUrl);
|
||||||
|
const pastedUrl = new URL(parsed.baseUrl);
|
||||||
|
return configuredUrl.host === pastedUrl.host;
|
||||||
|
} catch (e) {
|
||||||
|
// If URL parsing fails, allow the conversion
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the iframe embed HTML for a MediaCMS video.
|
||||||
|
*
|
||||||
|
* @param {Object} parsed - Parsed URL info
|
||||||
|
* @param {Object} options - Embed options
|
||||||
|
* @returns {string} - The iframe HTML
|
||||||
|
*/
|
||||||
|
const generateEmbedHtml = (parsed, options = {}) => {
|
||||||
|
// Build the embed URL with default options
|
||||||
|
const embedUrl = new URL(`${parsed.baseUrl}/embed`);
|
||||||
|
embedUrl.searchParams.set('m', parsed.videoId);
|
||||||
|
|
||||||
|
// Apply default options (all enabled by default for best user experience)
|
||||||
|
embedUrl.searchParams.set('showTitle', options.showTitle !== false ? '1' : '0');
|
||||||
|
embedUrl.searchParams.set('showRelated', options.showRelated !== false ? '1' : '0');
|
||||||
|
embedUrl.searchParams.set('showUserAvatar', options.showUserAvatar !== false ? '1' : '0');
|
||||||
|
embedUrl.searchParams.set('linkTitle', options.linkTitle !== false ? '1' : '0');
|
||||||
|
|
||||||
|
// Generate clean iframe HTML (wrapper will be added by editor for UI, then stripped on save)
|
||||||
|
const html = `<iframe ` +
|
||||||
|
`width="400" height="300" ` +
|
||||||
|
`style="display: block; border: 0;" ` +
|
||||||
|
`src="${embedUrl.toString()}" ` +
|
||||||
|
`allowfullscreen="allowfullscreen">` +
|
||||||
|
`</iframe>`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up auto-conversion for the editor.
|
||||||
|
* This registers event handlers to detect pasted MediaCMS URLs.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor - The TinyMCE editor instance
|
||||||
|
*/
|
||||||
|
export const setupAutoConvert = (editor) => {
|
||||||
|
const config = getData(editor) || {};
|
||||||
|
|
||||||
|
// Check if auto-convert is enabled (default: true)
|
||||||
|
if (config.autoConvertEnabled === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle paste events
|
||||||
|
editor.on('paste', (e) => {
|
||||||
|
handlePasteEvent(editor, e, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also handle input events for drag-and-drop text or keyboard paste
|
||||||
|
editor.on('input', (e) => {
|
||||||
|
handleInputEvent(editor, e, config);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle paste events to detect and convert MediaCMS URLs.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor - The TinyMCE editor instance
|
||||||
|
* @param {Event} e - The paste event
|
||||||
|
* @param {Object} config - Plugin configuration
|
||||||
|
*/
|
||||||
|
const handlePasteEvent = (editor, e, config) => {
|
||||||
|
// Get pasted text from clipboard
|
||||||
|
const clipboardData = e.clipboardData || window.clipboardData;
|
||||||
|
if (!clipboardData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get plain text first
|
||||||
|
const text = clipboardData.getData('text/plain') || clipboardData.getData('text');
|
||||||
|
if (!text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a MediaCMS URL
|
||||||
|
const parsed = parseMediaCMSUrl(text);
|
||||||
|
if (!parsed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if domain is allowed
|
||||||
|
if (!isDomainAllowed(parsed, config)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent default paste behavior
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
// Generate and insert the embed HTML
|
||||||
|
const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});
|
||||||
|
|
||||||
|
// Use a slight delay to ensure the editor is ready
|
||||||
|
setTimeout(() => {
|
||||||
|
editor.insertContent(embedHtml);
|
||||||
|
// Move cursor after the inserted content
|
||||||
|
editor.selection.collapse(false);
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle input events to catch URLs that might have been pasted without triggering paste event.
|
||||||
|
* This is a fallback for certain browsers/scenarios.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor - The TinyMCE editor instance
|
||||||
|
* @param {Event} e - The input event
|
||||||
|
* @param {Object} config - Plugin configuration
|
||||||
|
*/
|
||||||
|
const handleInputEvent = (editor, e, config) => {
|
||||||
|
// Only process inputType 'insertFromPaste' if paste event didn't catch it
|
||||||
|
if (e.inputType !== 'insertFromPaste' && e.inputType !== 'insertText') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current node and check if it contains just a URL
|
||||||
|
const node = editor.selection.getNode();
|
||||||
|
if (!node || node.nodeName !== 'P') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the paragraph contains only a MediaCMS URL
|
||||||
|
const text = node.textContent || '';
|
||||||
|
const parsed = parseMediaCMSUrl(text);
|
||||||
|
|
||||||
|
if (!parsed || !isDomainAllowed(parsed, config)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't convert if there's other content in the paragraph
|
||||||
|
const trimmedHtml = node.innerHTML.trim();
|
||||||
|
if (trimmedHtml !== text.trim() && !trimmedHtml.startsWith(text.trim())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the embed HTML
|
||||||
|
const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});
|
||||||
|
|
||||||
|
// Replace the paragraph content with the embed
|
||||||
|
// Use a slight delay to let the input event complete
|
||||||
|
setTimeout(() => {
|
||||||
|
// Re-check that the node still contains the URL (user might have typed more)
|
||||||
|
const currentText = node.textContent || '';
|
||||||
|
const currentParsed = parseMediaCMSUrl(currentText);
|
||||||
|
|
||||||
|
if (currentParsed && currentParsed.originalUrl === parsed.originalUrl) {
|
||||||
|
// Select and replace the entire node
|
||||||
|
editor.selection.select(node);
|
||||||
|
editor.insertContent(embedHtml);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a text is a MediaCMS URL (public helper).
|
||||||
|
*
|
||||||
|
* @param {string} text - The text to check
|
||||||
|
* @returns {boolean} - True if it's a MediaCMS URL
|
||||||
|
*/
|
||||||
|
export const isMediaCMSUrl = (text) => {
|
||||||
|
return parseMediaCMSUrl(text) !== null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a MediaCMS URL to embed HTML (public helper).
|
||||||
|
*
|
||||||
|
* @param {string} url - The MediaCMS URL
|
||||||
|
* @param {Object} options - Embed options
|
||||||
|
* @returns {string|null} - The embed HTML or null if not a valid URL
|
||||||
|
*/
|
||||||
|
export const convertToEmbed = (url, options = {}) => {
|
||||||
|
const parsed = parseMediaCMSUrl(url);
|
||||||
|
if (!parsed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return generateEmbedHtml(parsed, options);
|
||||||
|
};
|
||||||
282
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/commands.js
Executable file
282
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/commands.js
Executable file
@@ -0,0 +1,282 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny Media commands.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/commands
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {getStrings} from 'core/str';
|
||||||
|
import {
|
||||||
|
component,
|
||||||
|
iframeButtonName,
|
||||||
|
iframeMenuItemName,
|
||||||
|
iframeIcon,
|
||||||
|
} from './common';
|
||||||
|
import IframeEmbed from './iframeembed';
|
||||||
|
import {getButtonImage} from 'editor_tiny/utils';
|
||||||
|
|
||||||
|
const isIframe = (node) => node.nodeName.toLowerCase() === 'iframe' ||
|
||||||
|
(node.classList && node.classList.contains('tiny-iframe-responsive')) ||
|
||||||
|
(node.classList && node.classList.contains('tiny-mediacms-iframe-wrapper'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap iframes with overlay containers that allow hover detection.
|
||||||
|
* Since iframes capture mouse events, we add an invisible overlay on top
|
||||||
|
* that shows the edit button on hover.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor - The editor instance
|
||||||
|
* @param {Function} handleIframeAction - The action to perform when clicking the button
|
||||||
|
*/
|
||||||
|
const setupIframeOverlays = (editor, handleIframeAction) => {
|
||||||
|
/**
|
||||||
|
* Process all iframes in the editor and add overlay wrappers.
|
||||||
|
*/
|
||||||
|
const processIframes = () => {
|
||||||
|
const editorBody = editor.getBody();
|
||||||
|
if (!editorBody) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iframes = editorBody.querySelectorAll('iframe');
|
||||||
|
iframes.forEach((iframe) => {
|
||||||
|
// Skip if already wrapped
|
||||||
|
if (iframe.parentElement?.classList.contains('tiny-mediacms-iframe-wrapper')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip TinyMCE internal iframes
|
||||||
|
if (iframe.hasAttribute('data-mce-object') || iframe.hasAttribute('data-mce-placeholder')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create wrapper div
|
||||||
|
const wrapper = editor.getDoc().createElement('div');
|
||||||
|
wrapper.className = 'tiny-mediacms-iframe-wrapper';
|
||||||
|
wrapper.setAttribute('contenteditable', 'false');
|
||||||
|
|
||||||
|
// Create edit button (positioned inside wrapper, over the iframe)
|
||||||
|
const editBtn = editor.getDoc().createElement('button');
|
||||||
|
editBtn.className = 'tiny-mediacms-edit-btn';
|
||||||
|
editBtn.setAttribute('type', 'button');
|
||||||
|
editBtn.setAttribute('title', 'Edit video embed options');
|
||||||
|
// Use clean inline SVG to avoid TinyMCE wrapper issues
|
||||||
|
editBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">' +
|
||||||
|
'<circle cx="50" cy="50" r="48" fill="#2EAF5A"/>' +
|
||||||
|
'<polygon points="38,28 38,72 75,50" fill="#FFFFFF"/>' +
|
||||||
|
'</svg>';
|
||||||
|
|
||||||
|
// Wrap the iframe: insert wrapper, move iframe into it, add button
|
||||||
|
iframe.parentNode.insertBefore(wrapper, iframe);
|
||||||
|
wrapper.appendChild(iframe);
|
||||||
|
wrapper.appendChild(editBtn);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add CSS styles for hover effects to the editor's document.
|
||||||
|
*/
|
||||||
|
const addStyles = () => {
|
||||||
|
const editorDoc = editor.getDoc();
|
||||||
|
if (!editorDoc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if styles already added
|
||||||
|
if (editorDoc.getElementById('tiny-mediacms-overlay-styles')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = editorDoc.createElement('style');
|
||||||
|
style.id = 'tiny-mediacms-overlay-styles';
|
||||||
|
style.textContent = `
|
||||||
|
.tiny-mediacms-iframe-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.tiny-mediacms-iframe-wrapper iframe {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.tiny-mediacms-edit-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 48px;
|
||||||
|
left: 6px;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 10;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: 0 2px 6px rgba(0,0,0,0.35);
|
||||||
|
transition: transform 0.15s, box-shadow 0.15s;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.tiny-mediacms-edit-btn:hover {
|
||||||
|
transform: scale(1.15);
|
||||||
|
box-shadow: 0 3px 10px rgba(0,0,0,0.45);
|
||||||
|
}
|
||||||
|
.tiny-mediacms-edit-btn svg {
|
||||||
|
width: 18px !important;
|
||||||
|
height: 18px !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
editorDoc.head.appendChild(style);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle click on the edit button.
|
||||||
|
*
|
||||||
|
* @param {Event} e - The click event
|
||||||
|
*/
|
||||||
|
const handleOverlayClick = (e) => {
|
||||||
|
const target = e.target;
|
||||||
|
|
||||||
|
// Check if clicked on edit button or its child (svg/path)
|
||||||
|
const editBtn = target.closest('.tiny-mediacms-edit-btn');
|
||||||
|
if (!editBtn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
// Find the associated wrapper and iframe
|
||||||
|
const wrapper = editBtn.closest('.tiny-mediacms-iframe-wrapper');
|
||||||
|
if (!wrapper) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iframe = wrapper.querySelector('iframe');
|
||||||
|
if (!iframe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the wrapper so TinyMCE knows which element is selected
|
||||||
|
editor.selection.select(wrapper);
|
||||||
|
|
||||||
|
// Open the edit dialog
|
||||||
|
handleIframeAction();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setup on editor init
|
||||||
|
editor.on('init', () => {
|
||||||
|
addStyles();
|
||||||
|
processIframes();
|
||||||
|
|
||||||
|
// Handle clicks on the overlay
|
||||||
|
editor.getBody().addEventListener('click', handleOverlayClick);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-process when content changes
|
||||||
|
editor.on('SetContent', () => {
|
||||||
|
processIframes();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-process when content is pasted
|
||||||
|
editor.on('PastePostProcess', () => {
|
||||||
|
setTimeout(processIframes, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-process after undo/redo
|
||||||
|
editor.on('Undo Redo', () => {
|
||||||
|
processIframes();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-process on any content change (covers modal updates)
|
||||||
|
editor.on('Change', () => {
|
||||||
|
setTimeout(processIframes, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-process when node changes (selection changes)
|
||||||
|
editor.on('NodeChange', () => {
|
||||||
|
processIframes();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const registerIframeCommand = (editor, iframeButtonText, iframeButtonImage) => {
|
||||||
|
const handleIframeAction = () => {
|
||||||
|
const iframeEmbed = new IframeEmbed(editor);
|
||||||
|
iframeEmbed.displayDialogue();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register the iframe icon
|
||||||
|
editor.ui.registry.addIcon(iframeIcon, iframeButtonImage.html);
|
||||||
|
|
||||||
|
// Register the Menu Button as a toggle.
|
||||||
|
// This means that when highlighted over an existing iframe element it will show as toggled on.
|
||||||
|
editor.ui.registry.addToggleButton(iframeButtonName, {
|
||||||
|
icon: iframeIcon,
|
||||||
|
tooltip: iframeButtonText,
|
||||||
|
onAction: handleIframeAction,
|
||||||
|
onSetup: api => {
|
||||||
|
return editor.selection.selectorChangedWithUnbind(
|
||||||
|
'iframe:not([data-mce-object]):not([data-mce-placeholder]),.tiny-iframe-responsive,.tiny-mediacms-iframe-wrapper',
|
||||||
|
api.setActive
|
||||||
|
).unbind;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.ui.registry.addMenuItem(iframeMenuItemName, {
|
||||||
|
icon: iframeIcon,
|
||||||
|
text: iframeButtonText,
|
||||||
|
onAction: handleIframeAction,
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.ui.registry.addContextToolbar(iframeButtonName, {
|
||||||
|
predicate: isIframe,
|
||||||
|
items: iframeButtonName,
|
||||||
|
position: 'node',
|
||||||
|
scope: 'node'
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.ui.registry.addContextMenu(iframeButtonName, {
|
||||||
|
update: isIframe,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Setup iframe overlays with edit button on hover
|
||||||
|
setupIframeOverlays(editor, handleIframeAction);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSetup = async() => {
|
||||||
|
const [
|
||||||
|
iframeButtonText,
|
||||||
|
] = await getStrings([
|
||||||
|
'iframebuttontitle',
|
||||||
|
].map((key) => ({key, component})));
|
||||||
|
|
||||||
|
const [
|
||||||
|
iframeButtonImage,
|
||||||
|
] = await Promise.all([
|
||||||
|
getButtonImage('icon', component),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Note: The function returned here must be synchronous and cannot use promises.
|
||||||
|
// All promises must be resolved prior to returning the function.
|
||||||
|
return (editor) => {
|
||||||
|
registerIframeCommand(editor, iframeButtonText, iframeButtonImage);
|
||||||
|
};
|
||||||
|
};
|
||||||
30
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/common.js
Executable file
30
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/common.js
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny Media common values.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/common
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
pluginName: 'tiny_mediacms/plugin',
|
||||||
|
component: 'tiny_mediacms',
|
||||||
|
iframeButtonName: 'tiny_mediacms_iframe',
|
||||||
|
iframeMenuItemName: 'tiny_mediacms_iframe',
|
||||||
|
iframeIcon: 'tiny_mediacms_iframe',
|
||||||
|
};
|
||||||
60
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/configuration.js
Executable file
60
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/configuration.js
Executable file
@@ -0,0 +1,60 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny Media configuration.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/configuration
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
iframeButtonName,
|
||||||
|
iframeMenuItemName,
|
||||||
|
} from './common';
|
||||||
|
import {
|
||||||
|
addContextmenuItem,
|
||||||
|
} from 'editor_tiny/utils';
|
||||||
|
|
||||||
|
const configureMenu = (menu) => {
|
||||||
|
// Add the Iframe Embed to the insert menu.
|
||||||
|
menu.insert.items = `${iframeMenuItemName} ${menu.insert.items}`;
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
};
|
||||||
|
|
||||||
|
const configureToolbar = (toolbar) => {
|
||||||
|
// The toolbar contains an array of named sections.
|
||||||
|
// The Moodle integration ensures that there is a section called 'content'.
|
||||||
|
|
||||||
|
return toolbar.map((section) => {
|
||||||
|
if (section.name === 'content') {
|
||||||
|
// Insert the iframe button at the start of it.
|
||||||
|
section.items.unshift(iframeButtonName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return section;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const configure = (instanceConfig) => {
|
||||||
|
// Update the instance configuration to add the Iframe Embed menu option to the menus and toolbars.
|
||||||
|
return {
|
||||||
|
contextmenu: addContextmenuItem(instanceConfig.contextmenu, iframeButtonName),
|
||||||
|
menu: configureMenu(instanceConfig.menu),
|
||||||
|
toolbar: configureToolbar(instanceConfig.toolbar),
|
||||||
|
};
|
||||||
|
};
|
||||||
467
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embed.js
Executable file
467
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embed.js
Executable file
@@ -0,0 +1,467 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny Media plugin Embed class for Moodle.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/embed
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Templates from 'core/templates';
|
||||||
|
import {
|
||||||
|
getString,
|
||||||
|
getStrings,
|
||||||
|
} from 'core/str';
|
||||||
|
import * as ModalEvents from 'core/modal_events';
|
||||||
|
import {displayFilepicker} from 'editor_tiny/utils';
|
||||||
|
import {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options';
|
||||||
|
import {component} from "./common";
|
||||||
|
import EmbedModal from './embedmodal';
|
||||||
|
import Selectors from './selectors';
|
||||||
|
import {getEmbedPermissions} from './options';
|
||||||
|
import {getFilePicker} from 'editor_tiny/options';
|
||||||
|
|
||||||
|
export default class MediaEmbed {
|
||||||
|
editor = null;
|
||||||
|
canShowFilePicker = false;
|
||||||
|
canShowFilePickerPoster = false;
|
||||||
|
canShowFilePickerTrack = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {Object} The names of the alignment options.
|
||||||
|
*/
|
||||||
|
helpStrings = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {boolean} Indicate that the user is updating the media or not.
|
||||||
|
*/
|
||||||
|
isUpdating = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {Object} The currently selected media.
|
||||||
|
*/
|
||||||
|
selectedMedia = null;
|
||||||
|
|
||||||
|
constructor(editor) {
|
||||||
|
const permissions = getEmbedPermissions(editor);
|
||||||
|
|
||||||
|
// Indicates whether the file picker can be shown.
|
||||||
|
this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'media') !== 'undefined');
|
||||||
|
this.canShowFilePickerPoster = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined');
|
||||||
|
this.canShowFilePickerTrack = permissions.filepicker && (typeof getFilePicker(editor, 'subtitle') !== 'undefined');
|
||||||
|
|
||||||
|
this.editor = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getHelpStrings() {
|
||||||
|
if (!this.helpStrings) {
|
||||||
|
const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([
|
||||||
|
'addsource_help',
|
||||||
|
'tracks_help',
|
||||||
|
'subtitles_help',
|
||||||
|
'captions_help',
|
||||||
|
'descriptions_help',
|
||||||
|
'chapters_help',
|
||||||
|
'metadata_help',
|
||||||
|
].map((key) => ({
|
||||||
|
key,
|
||||||
|
component,
|
||||||
|
})));
|
||||||
|
|
||||||
|
this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata};
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.helpStrings;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTemplateContext(data) {
|
||||||
|
const languages = this.prepareMoodleLang();
|
||||||
|
|
||||||
|
const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => {
|
||||||
|
data[`${key.toLowerCase()}helpicon`] = {text};
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.assign({}, {
|
||||||
|
elementid: this.editor.getElement().id,
|
||||||
|
showfilepicker: this.canShowFilePicker,
|
||||||
|
showfilepickerposter: this.canShowFilePickerPoster,
|
||||||
|
showfilepickertrack: this.canShowFilePickerTrack,
|
||||||
|
langsinstalled: languages.installed,
|
||||||
|
langsavailable: languages.available,
|
||||||
|
link: true,
|
||||||
|
video: false,
|
||||||
|
audio: false,
|
||||||
|
isupdating: this.isUpdating,
|
||||||
|
}, data, helpIcons);
|
||||||
|
}
|
||||||
|
|
||||||
|
async displayDialogue() {
|
||||||
|
this.selectedMedia = this.getSelectedMedia();
|
||||||
|
const data = Object.assign({}, this.getCurrentEmbedData());
|
||||||
|
this.isUpdating = Object.keys(data).length !== 0;
|
||||||
|
|
||||||
|
this.currentModal = await EmbedModal.create({
|
||||||
|
title: getString('createmedia', 'tiny_mediacms'),
|
||||||
|
templateContext: await this.getTemplateContext(data),
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.registerEventListeners(this.currentModal);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentEmbedData() {
|
||||||
|
const properties = this.getMediumProperties();
|
||||||
|
if (!properties) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedProperties = {};
|
||||||
|
processedProperties[properties.type.toLowerCase()] = properties;
|
||||||
|
processedProperties.link = false;
|
||||||
|
|
||||||
|
return processedProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedMedia() {
|
||||||
|
const mediaElm = this.editor.selection.getNode();
|
||||||
|
|
||||||
|
if (!mediaElm) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') {
|
||||||
|
return mediaElm;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mediaElm.querySelector('video')) {
|
||||||
|
return mediaElm.querySelector('video');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mediaElm.querySelector('audio')) {
|
||||||
|
return mediaElm.querySelector('audio');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMediumProperties() {
|
||||||
|
const boolAttr = (elem, attr) => {
|
||||||
|
// As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.
|
||||||
|
// So in order to check if attr="true", we need to check if the attribute exists and if the value is empty or true.
|
||||||
|
return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));
|
||||||
|
};
|
||||||
|
|
||||||
|
const tracks = {
|
||||||
|
subtitles: [],
|
||||||
|
captions: [],
|
||||||
|
descriptions: [],
|
||||||
|
chapters: [],
|
||||||
|
metadata: []
|
||||||
|
};
|
||||||
|
const sources = [];
|
||||||
|
|
||||||
|
const medium = this.selectedMedia;
|
||||||
|
if (!medium) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
medium.querySelectorAll('track').forEach((track) => {
|
||||||
|
tracks[track.getAttribute('kind')].push({
|
||||||
|
src: track.getAttribute('src'),
|
||||||
|
srclang: track.getAttribute('srclang'),
|
||||||
|
label: track.getAttribute('label'),
|
||||||
|
defaultTrack: boolAttr(track, 'default')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
medium.querySelectorAll('source').forEach((source) => {
|
||||||
|
sources.push(source.src);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio,
|
||||||
|
sources,
|
||||||
|
poster: medium.getAttribute('poster'),
|
||||||
|
title: medium.getAttribute('title'),
|
||||||
|
width: medium.getAttribute('width'),
|
||||||
|
height: medium.getAttribute('height'),
|
||||||
|
autoplay: boolAttr(medium, 'autoplay'),
|
||||||
|
loop: boolAttr(medium, 'loop'),
|
||||||
|
muted: boolAttr(medium, 'muted'),
|
||||||
|
controls: boolAttr(medium, 'controls'),
|
||||||
|
tracks,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareMoodleLang() {
|
||||||
|
const moodleLangs = getMoodleLang(this.editor);
|
||||||
|
const currentLanguage = getCurrentLanguage(this.editor);
|
||||||
|
|
||||||
|
const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({
|
||||||
|
lang,
|
||||||
|
code,
|
||||||
|
"default": lang === currentLanguage,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({
|
||||||
|
lang,
|
||||||
|
code,
|
||||||
|
"default": lang === currentLanguage,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
installed,
|
||||||
|
available,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getMoodleLangObj(subtitleLang) {
|
||||||
|
const {available} = getMoodleLang(this.editor);
|
||||||
|
|
||||||
|
if (available[subtitleLang]) {
|
||||||
|
return {
|
||||||
|
lang: subtitleLang,
|
||||||
|
code: available[subtitleLang],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
filePickerCallback(params, element, fpType) {
|
||||||
|
if (params.url !== '') {
|
||||||
|
const tabPane = element.closest('.tab-pane');
|
||||||
|
element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;
|
||||||
|
|
||||||
|
if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) {
|
||||||
|
tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fpType === 'subtitle') {
|
||||||
|
// If the file is subtitle file. We need to match the language and label for that file.
|
||||||
|
const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0];
|
||||||
|
const langObj = this.getMoodleLangObj(subtitleLang);
|
||||||
|
if (langObj) {
|
||||||
|
const track = element.closest(Selectors.EMBED.elements.track);
|
||||||
|
track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim();
|
||||||
|
track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addMediaSourceComponent(element, callback) {
|
||||||
|
const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);
|
||||||
|
const clone = sourceElement.cloneNode(true);
|
||||||
|
|
||||||
|
sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');
|
||||||
|
sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden');
|
||||||
|
|
||||||
|
sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling);
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(clone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeMediaSourceComponent(element) {
|
||||||
|
const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);
|
||||||
|
sourceElement.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
addTrackComponent(element, callback) {
|
||||||
|
const trackElement = element.closest(Selectors.EMBED.elements.track);
|
||||||
|
const clone = trackElement.cloneNode(true);
|
||||||
|
|
||||||
|
trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');
|
||||||
|
trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden');
|
||||||
|
|
||||||
|
trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(clone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTrackComponent(element) {
|
||||||
|
const sourceElement = element.closest(Selectors.EMBED.elements.track);
|
||||||
|
sourceElement.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
getMediumTypeFromTabPane(tabPane) {
|
||||||
|
return tabPane.getAttribute('data-medium-type');
|
||||||
|
}
|
||||||
|
|
||||||
|
getTrackTypeFromTabPane(tabPane) {
|
||||||
|
return tabPane.getAttribute('data-track-kind');
|
||||||
|
}
|
||||||
|
|
||||||
|
getMediaHTML(form) {
|
||||||
|
const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active'));
|
||||||
|
const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']);
|
||||||
|
|
||||||
|
return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMediaHTMLLink(tab) {
|
||||||
|
const context = {
|
||||||
|
url: tab.querySelector(Selectors.EMBED.elements.url).value,
|
||||||
|
name: tab.querySelector(Selectors.EMBED.elements.name).value || false
|
||||||
|
};
|
||||||
|
|
||||||
|
return context.url ? Templates.renderForPromise('tiny_mediacms/embed_media_link', context) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getMediaHTMLVideo(tab) {
|
||||||
|
const context = this.getContextForMediaHTML(tab);
|
||||||
|
context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false;
|
||||||
|
context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false;
|
||||||
|
context.poster = tab.querySelector(
|
||||||
|
`${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}`
|
||||||
|
).value || false;
|
||||||
|
|
||||||
|
return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_video', context) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getMediaHTMLAudio(tab) {
|
||||||
|
const context = this.getContextForMediaHTML(tab);
|
||||||
|
|
||||||
|
return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_audio', context) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getContextForMediaHTML(tab) {
|
||||||
|
const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({
|
||||||
|
track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value,
|
||||||
|
kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')),
|
||||||
|
label: track.querySelector(Selectors.EMBED.elements.trackLabel).value ||
|
||||||
|
track.querySelector(Selectors.EMBED.elements.trackLang).value,
|
||||||
|
srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value,
|
||||||
|
defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? "true" : null
|
||||||
|
})).filter((track) => !!track.track);
|
||||||
|
|
||||||
|
const sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' '
|
||||||
|
+ Selectors.EMBED.elements.url))
|
||||||
|
.filter((source) => !!source.value)
|
||||||
|
.map((source) => source.value);
|
||||||
|
|
||||||
|
return {
|
||||||
|
sources,
|
||||||
|
description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' '
|
||||||
|
+ Selectors.EMBED.elements.url).value || false,
|
||||||
|
tracks,
|
||||||
|
showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked,
|
||||||
|
autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,
|
||||||
|
muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked,
|
||||||
|
loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked,
|
||||||
|
title: tab.querySelector(Selectors.EMBED.elements.title).value || false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilepickerTypeFromElement(element) {
|
||||||
|
if (element.closest(Selectors.EMBED.elements.posterSource)) {
|
||||||
|
return 'image';
|
||||||
|
}
|
||||||
|
if (element.closest(Selectors.EMBED.elements.trackSource)) {
|
||||||
|
return 'subtitle';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'media';
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickHandler(e) {
|
||||||
|
const element = e.target;
|
||||||
|
|
||||||
|
const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser);
|
||||||
|
if (mediaBrowser) {
|
||||||
|
e.preventDefault();
|
||||||
|
const fpType = this.getFilepickerTypeFromElement(element);
|
||||||
|
const params = await displayFilepicker(this.editor, fpType);
|
||||||
|
this.filePickerCallback(params, element, fpType);
|
||||||
|
}
|
||||||
|
|
||||||
|
const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent');
|
||||||
|
if (addComponentSourceAction) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.addMediaSourceComponent(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent');
|
||||||
|
if (removeComponentSourceAction) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.removeMediaSourceComponent(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');
|
||||||
|
if (addComponentTrackAction) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.addTrackComponent(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent');
|
||||||
|
if (removeComponentTrackAction) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.removeTrackComponent(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow one track per tab to be selected as "default".
|
||||||
|
const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault);
|
||||||
|
if (trackDefaultAction && trackDefaultAction.checked) {
|
||||||
|
const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane'));
|
||||||
|
|
||||||
|
element.parentElement
|
||||||
|
.closest('.root.tab-content')
|
||||||
|
.querySelectorAll(Selectors.EMBED.elements.trackDefault)
|
||||||
|
.forEach((select) => {
|
||||||
|
if (select !== element && getKind(element) === getKind(select)) {
|
||||||
|
select.checked = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleDialogueSubmission(event, modal) {
|
||||||
|
const {html} = await this.getMediaHTML(modal.getRoot()[0]);
|
||||||
|
if (html) {
|
||||||
|
if (this.isUpdating) {
|
||||||
|
this.selectedMedia.outerHTML = html;
|
||||||
|
this.isUpdating = false;
|
||||||
|
} else {
|
||||||
|
this.editor.insertContent(html);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async registerEventListeners(modal) {
|
||||||
|
await modal.getBody();
|
||||||
|
const $root = modal.getRoot();
|
||||||
|
const root = $root[0];
|
||||||
|
if (this.canShowFilePicker || this.canShowFilePickerPoster || this.canShowFilePickerTrack) {
|
||||||
|
root.addEventListener('click', this.clickHandler.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
$root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));
|
||||||
|
$root.on(ModalEvents.hidden, () => {
|
||||||
|
this.currentModal.destroy();
|
||||||
|
});
|
||||||
|
$root.on(ModalEvents.shown, () => {
|
||||||
|
root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => {
|
||||||
|
const defaultVal = dropdown.getAttribute('data-value');
|
||||||
|
if (defaultVal) {
|
||||||
|
dropdown.value = defaultVal;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
47
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embedmodal.js
Executable file
47
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embedmodal.js
Executable file
@@ -0,0 +1,47 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embedded Media Management Modal for Tiny.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/embedmodal
|
||||||
|
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Modal from 'core/modal';
|
||||||
|
import {component} from './common';
|
||||||
|
|
||||||
|
export default class EmbedModal extends Modal {
|
||||||
|
static TYPE = `${component}/modal`;
|
||||||
|
static TEMPLATE = `${component}/embed_media_modal`;
|
||||||
|
|
||||||
|
registerEventListeners() {
|
||||||
|
// Call the parent registration.
|
||||||
|
super.registerEventListeners();
|
||||||
|
|
||||||
|
// Register to close on save/cancel.
|
||||||
|
this.registerCloseOnSave();
|
||||||
|
this.registerCloseOnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(modalConfig) {
|
||||||
|
modalConfig.large = true;
|
||||||
|
modalConfig.removeOnClose = true;
|
||||||
|
modalConfig.show = true;
|
||||||
|
|
||||||
|
super.configure(modalConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
1090
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframeembed.js
Executable file
1090
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframeembed.js
Executable file
File diff suppressed because it is too large
Load Diff
47
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframemodal.js
Executable file
47
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframemodal.js
Executable file
@@ -0,0 +1,47 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iframe Embed Modal for Tiny Media2.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/iframemodal
|
||||||
|
* @copyright 2024
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Modal from 'core/modal';
|
||||||
|
import {component} from './common';
|
||||||
|
|
||||||
|
export default class IframeModal extends Modal {
|
||||||
|
static TYPE = `${component}/iframemodal`;
|
||||||
|
static TEMPLATE = `${component}/iframe_embed_modal`;
|
||||||
|
|
||||||
|
registerEventListeners() {
|
||||||
|
// Call the parent registration.
|
||||||
|
super.registerEventListeners();
|
||||||
|
|
||||||
|
// Register to close on save/cancel.
|
||||||
|
this.registerCloseOnSave();
|
||||||
|
this.registerCloseOnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(modalConfig) {
|
||||||
|
modalConfig.large = true;
|
||||||
|
modalConfig.removeOnClose = true;
|
||||||
|
modalConfig.show = true;
|
||||||
|
|
||||||
|
super.configure(modalConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
273
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/image.js
Executable file
273
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/image.js
Executable file
@@ -0,0 +1,273 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny Media plugin Image class for Moodle.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/image
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Selectors from './selectors';
|
||||||
|
import ImageModal from './imagemodal';
|
||||||
|
import {getImagePermissions} from './options';
|
||||||
|
import {getFilePicker} from 'editor_tiny/options';
|
||||||
|
import {ImageInsert} from 'tiny_mediacms/imageinsert';
|
||||||
|
import {ImageDetails} from 'tiny_mediacms/imagedetails';
|
||||||
|
import {prefetchStrings} from 'core/prefetch';
|
||||||
|
import {getString} from 'core/str';
|
||||||
|
import {
|
||||||
|
bodyImageInsert,
|
||||||
|
footerImageInsert,
|
||||||
|
bodyImageDetails,
|
||||||
|
footerImageDetails,
|
||||||
|
showElements,
|
||||||
|
hideElements,
|
||||||
|
isPercentageValue,
|
||||||
|
} from 'tiny_mediacms/imagehelpers';
|
||||||
|
|
||||||
|
prefetchStrings('tiny_mediacms', [
|
||||||
|
'imageurlrequired',
|
||||||
|
'sizecustom_help',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export default class MediaImage {
|
||||||
|
canShowFilePicker = false;
|
||||||
|
editor = null;
|
||||||
|
currentModal = null;
|
||||||
|
/**
|
||||||
|
* @type {HTMLElement|null} The root element.
|
||||||
|
*/
|
||||||
|
root = null;
|
||||||
|
|
||||||
|
constructor(editor) {
|
||||||
|
const permissions = getImagePermissions(editor);
|
||||||
|
const options = getFilePicker(editor, 'image');
|
||||||
|
// Indicates whether the file picker can be shown.
|
||||||
|
this.canShowFilePicker = permissions.filepicker
|
||||||
|
&& (typeof options !== 'undefined')
|
||||||
|
&& Object.keys(options.repositories).length > 0;
|
||||||
|
// Indicates whether the drop zone area can be shown.
|
||||||
|
this.canShowDropZone = (typeof options !== 'undefined') &&
|
||||||
|
Object.values(options.repositories).some(repository => repository.type === 'upload');
|
||||||
|
|
||||||
|
this.editor = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
async displayDialogue() {
|
||||||
|
const currentImageData = await this.getCurrentImageData();
|
||||||
|
this.currentModal = await ImageModal.create();
|
||||||
|
this.root = this.currentModal.getRoot()[0];
|
||||||
|
if (currentImageData && currentImageData.src) {
|
||||||
|
this.loadPreviewImage(currentImageData.src);
|
||||||
|
} else {
|
||||||
|
this.loadInsertImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays an insert image view asynchronously.
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
loadInsertImage = async function() {
|
||||||
|
const templateContext = {
|
||||||
|
elementid: this.editor.id,
|
||||||
|
showfilepicker: this.canShowFilePicker,
|
||||||
|
showdropzone: this.canShowDropZone,
|
||||||
|
};
|
||||||
|
|
||||||
|
Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])
|
||||||
|
.then(() => {
|
||||||
|
const imageinsert = new ImageInsert(
|
||||||
|
this.root,
|
||||||
|
this.editor,
|
||||||
|
this.currentModal,
|
||||||
|
this.canShowFilePicker,
|
||||||
|
this.canShowDropZone,
|
||||||
|
);
|
||||||
|
imageinsert.init();
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
async getTemplateContext(data) {
|
||||||
|
return {
|
||||||
|
elementid: this.editor.id,
|
||||||
|
showfilepicker: this.canShowFilePicker,
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCurrentImageData() {
|
||||||
|
const selectedImageProperties = this.getSelectedImageProperties();
|
||||||
|
if (!selectedImageProperties) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const properties = {...selectedImageProperties};
|
||||||
|
|
||||||
|
if (properties.src) {
|
||||||
|
properties.haspreview = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!properties.alt) {
|
||||||
|
properties.presentation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously loads and previews an image from the provided URL.
|
||||||
|
*
|
||||||
|
* @param {string} url - The URL of the image to load and preview.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
loadPreviewImage = async function(url) {
|
||||||
|
this.startImageLoading();
|
||||||
|
const image = new Image();
|
||||||
|
image.src = url;
|
||||||
|
image.addEventListener('error', async() => {
|
||||||
|
const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);
|
||||||
|
urlWarningLabelEle.innerHTML = await getString('imageurlrequired', 'tiny_mediacms');
|
||||||
|
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||||
|
this.stopImageLoading();
|
||||||
|
});
|
||||||
|
|
||||||
|
image.addEventListener('load', async() => {
|
||||||
|
const currentImageData = await this.getCurrentImageData();
|
||||||
|
let templateContext = await this.getTemplateContext(currentImageData);
|
||||||
|
templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_mediacms')};
|
||||||
|
|
||||||
|
Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])
|
||||||
|
.then(() => {
|
||||||
|
this.stopImageLoading();
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
const imagedetails = new ImageDetails(
|
||||||
|
this.root,
|
||||||
|
this.editor,
|
||||||
|
this.currentModal,
|
||||||
|
this.canShowFilePicker,
|
||||||
|
this.canShowDropZone,
|
||||||
|
url,
|
||||||
|
image,
|
||||||
|
);
|
||||||
|
imagedetails.init();
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getSelectedImageProperties() {
|
||||||
|
const image = this.getSelectedImage();
|
||||||
|
if (!image) {
|
||||||
|
this.selectedImage = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const properties = {
|
||||||
|
src: null,
|
||||||
|
alt: null,
|
||||||
|
width: null,
|
||||||
|
height: null,
|
||||||
|
presentation: false,
|
||||||
|
customStyle: '', // Custom CSS styles applied to the image.
|
||||||
|
};
|
||||||
|
|
||||||
|
const getImageHeight = (image) => {
|
||||||
|
if (!isPercentageValue(String(image.height))) {
|
||||||
|
return parseInt(image.height, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return image.height;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getImageWidth = (image) => {
|
||||||
|
if (!isPercentageValue(String(image.width))) {
|
||||||
|
return parseInt(image.width, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return image.width;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the current selection.
|
||||||
|
this.selectedImage = image;
|
||||||
|
|
||||||
|
properties.customStyle = image.style.cssText;
|
||||||
|
|
||||||
|
const width = getImageWidth(image);
|
||||||
|
if (width !== 0) {
|
||||||
|
properties.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
const height = getImageHeight(image);
|
||||||
|
if (height !== 0) {
|
||||||
|
properties.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
properties.src = image.getAttribute('src');
|
||||||
|
properties.alt = image.getAttribute('alt') || '';
|
||||||
|
properties.presentation = (image.getAttribute('role') === 'presentation');
|
||||||
|
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedImage() {
|
||||||
|
const imgElm = this.editor.selection.getNode();
|
||||||
|
const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');
|
||||||
|
if (figureElm) {
|
||||||
|
return this.editor.dom.select('img', figureElm)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return imgElm;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPlaceholderImage(imgElm) {
|
||||||
|
if (imgElm.nodeName.toUpperCase() !== 'IMG') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the upload loader and disables UI elements while loading a file.
|
||||||
|
*/
|
||||||
|
startImageLoading() {
|
||||||
|
showElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
||||||
|
hideElements(Selectors.IMAGE.elements.insertImage, this.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the upload loader and disables UI elements while loading a file.
|
||||||
|
*/
|
||||||
|
stopImageLoading() {
|
||||||
|
hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
||||||
|
showElements(Selectors.IMAGE.elements.insertImage, this.root);
|
||||||
|
}
|
||||||
|
}
|
||||||
614
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagedetails.js
Executable file
614
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagedetails.js
Executable file
@@ -0,0 +1,614 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny media plugin image details class for Moodle.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/imagedetails
|
||||||
|
* @copyright 2024 Meirza <meirza.arson@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Config from 'core/config';
|
||||||
|
import ModalEvents from 'core/modal_events';
|
||||||
|
import Notification from 'core/notification';
|
||||||
|
import Pending from 'core/pending';
|
||||||
|
import Selectors from './selectors';
|
||||||
|
import Templates from 'core/templates';
|
||||||
|
import {getString} from 'core/str';
|
||||||
|
import {ImageInsert} from 'tiny_mediacms/imageinsert';
|
||||||
|
import {
|
||||||
|
bodyImageInsert,
|
||||||
|
footerImageInsert,
|
||||||
|
showElements,
|
||||||
|
hideElements,
|
||||||
|
isPercentageValue,
|
||||||
|
} from 'tiny_mediacms/imagehelpers';
|
||||||
|
|
||||||
|
export class ImageDetails {
|
||||||
|
DEFAULTS = {
|
||||||
|
WIDTH: 160,
|
||||||
|
HEIGHT: 160,
|
||||||
|
};
|
||||||
|
|
||||||
|
rawImageDimensions = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
root,
|
||||||
|
editor,
|
||||||
|
currentModal,
|
||||||
|
canShowFilePicker,
|
||||||
|
canShowDropZone,
|
||||||
|
currentUrl,
|
||||||
|
image,
|
||||||
|
) {
|
||||||
|
this.root = root;
|
||||||
|
this.editor = editor;
|
||||||
|
this.currentModal = currentModal;
|
||||||
|
this.canShowFilePicker = canShowFilePicker;
|
||||||
|
this.canShowDropZone = canShowDropZone;
|
||||||
|
this.currentUrl = currentUrl;
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
init = function() {
|
||||||
|
this.currentModal.setTitle(getString('imagedetails', 'tiny_mediacms'));
|
||||||
|
this.imageTypeChecked();
|
||||||
|
this.presentationChanged();
|
||||||
|
this.storeImageDimensions(this.image);
|
||||||
|
this.setImageDimensions();
|
||||||
|
this.registerEventListeners();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads and displays a preview image based on the provided URL, and handles image loading events.
|
||||||
|
*/
|
||||||
|
loadInsertImage = async function() {
|
||||||
|
const templateContext = {
|
||||||
|
elementid: this.editor.id,
|
||||||
|
showfilepicker: this.canShowFilePicker,
|
||||||
|
showdropzone: this.canShowDropZone,
|
||||||
|
};
|
||||||
|
|
||||||
|
Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])
|
||||||
|
.then(() => {
|
||||||
|
const imageinsert = new ImageInsert(
|
||||||
|
this.root,
|
||||||
|
this.editor,
|
||||||
|
this.currentModal,
|
||||||
|
this.canShowFilePicker,
|
||||||
|
this.canShowDropZone,
|
||||||
|
);
|
||||||
|
imageinsert.init();
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
storeImageDimensions(image) {
|
||||||
|
// Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).
|
||||||
|
this.rawImageDimensions = {
|
||||||
|
width: image.width || this.DEFAULTS.WIDTH,
|
||||||
|
height: image.height || this.DEFAULTS.HEIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentWidth = (element) => {
|
||||||
|
if (element.value === '') {
|
||||||
|
element.value = this.rawImageDimensions.width;
|
||||||
|
}
|
||||||
|
return element.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentHeight = (element) => {
|
||||||
|
if (element.value === '') {
|
||||||
|
element.value = this.rawImageDimensions.height;
|
||||||
|
}
|
||||||
|
return element.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);
|
||||||
|
const currentWidth = getCurrentWidth(widthInput);
|
||||||
|
|
||||||
|
const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);
|
||||||
|
const currentHeight = getCurrentHeight(heightInput);
|
||||||
|
|
||||||
|
const preview = this.root.querySelector(Selectors.IMAGE.elements.preview);
|
||||||
|
preview.setAttribute('src', image.src);
|
||||||
|
preview.style.display = '';
|
||||||
|
|
||||||
|
// Ensure the checkbox always in unchecked status when an image loads at first.
|
||||||
|
const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain);
|
||||||
|
if (isPercentageValue(currentWidth) && isPercentageValue(currentHeight)) {
|
||||||
|
constrain.checked = currentWidth === currentHeight;
|
||||||
|
} else if (image.width === 0 || image.height === 0) {
|
||||||
|
// If we don't have both dimensions of the image, we can't auto-size it, so disable control.
|
||||||
|
constrain.disabled = 'disabled';
|
||||||
|
} else {
|
||||||
|
// This is the same as comparing to 3 decimal places.
|
||||||
|
const widthRatio = Math.round(100 * parseInt(currentWidth, 10) / image.width);
|
||||||
|
const heightRatio = Math.round(100 * parseInt(currentHeight, 10) / image.height);
|
||||||
|
constrain.checked = widthRatio === heightRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the selected size option based on current width and height values.
|
||||||
|
*
|
||||||
|
* @param {number} currentWidth - The current width value.
|
||||||
|
* @param {number} currentHeight - The current height value.
|
||||||
|
*/
|
||||||
|
const setSelectedSize = (currentWidth, currentHeight) => {
|
||||||
|
if (this.rawImageDimensions.width === currentWidth &&
|
||||||
|
this.rawImageDimensions.height === currentHeight
|
||||||
|
) {
|
||||||
|
this.currentWidth = this.rawImageDimensions.width;
|
||||||
|
this.currentHeight = this.rawImageDimensions.height;
|
||||||
|
this.sizeChecked('original');
|
||||||
|
} else {
|
||||||
|
this.currentWidth = currentWidth;
|
||||||
|
this.currentHeight = currentHeight;
|
||||||
|
this.sizeChecked('custom');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setSelectedSize(Number(currentWidth), Number(currentHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the selection of image size options and updates the form inputs accordingly.
|
||||||
|
*
|
||||||
|
* @param {string} option - The selected image size option ("original" or "custom").
|
||||||
|
*/
|
||||||
|
sizeChecked(option) {
|
||||||
|
const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);
|
||||||
|
const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);
|
||||||
|
if (option === "original") {
|
||||||
|
this.sizeOriginalChecked();
|
||||||
|
widthInput.value = this.rawImageDimensions.width;
|
||||||
|
heightInput.value = this.rawImageDimensions.height;
|
||||||
|
} else if (option === "custom") {
|
||||||
|
this.sizeCustomChecked();
|
||||||
|
widthInput.value = this.currentWidth;
|
||||||
|
heightInput.value = this.currentHeight;
|
||||||
|
|
||||||
|
// If the current size is equal to the original size, then check the Keep proportion checkbox.
|
||||||
|
if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) {
|
||||||
|
const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);
|
||||||
|
constrainField.checked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.autoAdjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
autoAdjustSize(forceHeight = false) {
|
||||||
|
// If we do not know the image size, do not do anything.
|
||||||
|
if (!this.rawImageDimensions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);
|
||||||
|
const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);
|
||||||
|
|
||||||
|
const normalizeFieldData = (fieldData) => {
|
||||||
|
fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value);
|
||||||
|
if (fieldData.isPercentageValue) {
|
||||||
|
fieldData.percentValue = parseInt(fieldData.field.value, 10);
|
||||||
|
fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;
|
||||||
|
} else {
|
||||||
|
fieldData.pixelSize = parseInt(fieldData.field.value, 10);
|
||||||
|
fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getKeyField = () => {
|
||||||
|
const getValue = () => {
|
||||||
|
if (forceHeight) {
|
||||||
|
return {
|
||||||
|
field: heightField,
|
||||||
|
type: 'height',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
field: widthField,
|
||||||
|
type: 'width',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentValue = getValue();
|
||||||
|
if (currentValue.field.value === '') {
|
||||||
|
currentValue.field.value = this.rawImageDimensions[currentValue.type];
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeFieldData(currentValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRelativeField = () => {
|
||||||
|
if (forceHeight) {
|
||||||
|
return normalizeFieldData({
|
||||||
|
field: widthField,
|
||||||
|
type: 'width',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return normalizeFieldData({
|
||||||
|
field: heightField,
|
||||||
|
type: 'height',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Now update with the new values.
|
||||||
|
const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);
|
||||||
|
if (constrainField.checked) {
|
||||||
|
const keyField = getKeyField();
|
||||||
|
const relativeField = getRelativeField();
|
||||||
|
// We are keeping the image in proportion.
|
||||||
|
// Calculate the size for the relative field.
|
||||||
|
if (keyField.isPercentageValue) {
|
||||||
|
// In proportion, so the percentages are the same.
|
||||||
|
relativeField.field.value = keyField.field.value;
|
||||||
|
relativeField.percentValue = keyField.percentValue;
|
||||||
|
} else {
|
||||||
|
relativeField.pixelSize = Math.round(
|
||||||
|
keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]
|
||||||
|
);
|
||||||
|
relativeField.field.value = relativeField.pixelSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the custom width and height to reuse.
|
||||||
|
this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth;
|
||||||
|
this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the dimensions of the image preview element based on user input and constraints.
|
||||||
|
*/
|
||||||
|
setImageDimensions = () => {
|
||||||
|
const imagePreviewBox = this.root.querySelector(Selectors.IMAGE.elements.previewBox);
|
||||||
|
const image = this.root.querySelector(Selectors.IMAGE.elements.preview);
|
||||||
|
const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);
|
||||||
|
const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);
|
||||||
|
|
||||||
|
const updateImageDimensions = () => {
|
||||||
|
// Get the latest dimensions of the preview box for responsiveness.
|
||||||
|
const boxWidth = imagePreviewBox.clientWidth;
|
||||||
|
const boxHeight = imagePreviewBox.clientHeight;
|
||||||
|
// Get the new width and height for the image.
|
||||||
|
const dimensions = this.fitSquareIntoBox(widthField.value, heightField.value, boxWidth, boxHeight);
|
||||||
|
image.style.width = `${dimensions.width}px`;
|
||||||
|
image.style.height = `${dimensions.height}px`;
|
||||||
|
};
|
||||||
|
// If the client size is zero, then get the new dimensions once the modal is shown.
|
||||||
|
if (imagePreviewBox.clientWidth === 0) {
|
||||||
|
// Call the shown event.
|
||||||
|
this.currentModal.getRoot().on(ModalEvents.shown, () => {
|
||||||
|
updateImageDimensions();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateImageDimensions();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the selection of the "Original Size" option and updates the form elements accordingly.
|
||||||
|
*/
|
||||||
|
sizeOriginalChecked() {
|
||||||
|
this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true;
|
||||||
|
this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false;
|
||||||
|
hideElements(Selectors.IMAGE.elements.properties, this.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the selection of the "Custom Size" option and updates the form elements accordingly.
|
||||||
|
*/
|
||||||
|
sizeCustomChecked() {
|
||||||
|
this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false;
|
||||||
|
this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true;
|
||||||
|
showElements(Selectors.IMAGE.elements.properties, this.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly.
|
||||||
|
*/
|
||||||
|
presentationChanged() {
|
||||||
|
const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation);
|
||||||
|
const alt = this.root.querySelector(Selectors.IMAGE.elements.alt);
|
||||||
|
alt.disabled = presentation.checked;
|
||||||
|
|
||||||
|
// Counting the image description characters.
|
||||||
|
this.handleKeyupCharacterCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function checks whether an image URL is local (within the same website's domain) or external (from an external source).
|
||||||
|
* Depending on the result, it dynamically updates the visibility and content of HTML elements in a user interface.
|
||||||
|
* If the image is local then we only show it's filename.
|
||||||
|
* If the image is external then it will show full URL and it can be updated.
|
||||||
|
*/
|
||||||
|
imageTypeChecked() {
|
||||||
|
const regex = new RegExp(`${Config.wwwroot}`);
|
||||||
|
|
||||||
|
// True if the URL is from external, otherwise false.
|
||||||
|
const isExternalUrl = regex.test(this.currentUrl) === false;
|
||||||
|
|
||||||
|
// Hide the URL input.
|
||||||
|
hideElements(Selectors.IMAGE.elements.url, this.root);
|
||||||
|
|
||||||
|
if (!isExternalUrl) {
|
||||||
|
// Split the URL by '/' to get an array of segments.
|
||||||
|
const segments = this.currentUrl.split('/');
|
||||||
|
// Get the last segment, which should be the filename.
|
||||||
|
const filename = segments.pop().split('?')[0];
|
||||||
|
// Show the file name.
|
||||||
|
this.setFilenameLabel(decodeURI(filename));
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.setFilenameLabel(decodeURI(this.currentUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the string for the URL label element.
|
||||||
|
*
|
||||||
|
* @param {string} label - The label text to set.
|
||||||
|
*/
|
||||||
|
setFilenameLabel(label) {
|
||||||
|
const urlLabelEle = this.root.querySelector(Selectors.IMAGE.elements.fileNameLabel);
|
||||||
|
if (urlLabelEle) {
|
||||||
|
urlLabelEle.innerHTML = label;
|
||||||
|
urlLabelEle.setAttribute("title", label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAriaInvalid(selectors, predicate) {
|
||||||
|
selectors.forEach((selector) => {
|
||||||
|
const elements = this.root.querySelectorAll(selector);
|
||||||
|
elements.forEach((element) => element.setAttribute('aria-invalid', predicate));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hasErrorUrlField() {
|
||||||
|
const urlError = this.currentUrl === '';
|
||||||
|
if (urlError) {
|
||||||
|
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||||
|
} else {
|
||||||
|
hideElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||||
|
}
|
||||||
|
this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);
|
||||||
|
|
||||||
|
return urlError;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasErrorAltField() {
|
||||||
|
const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;
|
||||||
|
const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked;
|
||||||
|
const imageAltError = alt === '' && !presentation;
|
||||||
|
if (imageAltError) {
|
||||||
|
showElements(Selectors.IMAGE.elements.altWarning, this.root);
|
||||||
|
} else {
|
||||||
|
hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root);
|
||||||
|
}
|
||||||
|
this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);
|
||||||
|
|
||||||
|
return imageAltError;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateWarning() {
|
||||||
|
const urlError = this.hasErrorUrlField();
|
||||||
|
const imageAltError = this.hasErrorAltField();
|
||||||
|
|
||||||
|
return urlError || imageAltError;
|
||||||
|
}
|
||||||
|
|
||||||
|
getImageContext() {
|
||||||
|
// Check if there are any accessibility issues.
|
||||||
|
if (this.updateWarning()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const classList = [];
|
||||||
|
const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain).checked;
|
||||||
|
const sizeOriginal = this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked;
|
||||||
|
if (constrain || sizeOriginal) {
|
||||||
|
// If the Auto size checkbox is checked or the Original size is checked, then apply the responsive class.
|
||||||
|
classList.push(Selectors.IMAGE.styles.responsive);
|
||||||
|
} else {
|
||||||
|
// Otherwise, remove it.
|
||||||
|
classList.pop(Selectors.IMAGE.styles.responsive);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: this.currentUrl,
|
||||||
|
alt: this.root.querySelector(Selectors.IMAGE.elements.alt).value,
|
||||||
|
width: this.root.querySelector(Selectors.IMAGE.elements.width).value,
|
||||||
|
height: this.root.querySelector(Selectors.IMAGE.elements.height).value,
|
||||||
|
presentation: this.root.querySelector(Selectors.IMAGE.elements.presentation).checked,
|
||||||
|
customStyle: this.root.querySelector(Selectors.IMAGE.elements.customStyle).value,
|
||||||
|
classlist: classList.join(' '),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setImage() {
|
||||||
|
const pendingPromise = new Pending('tiny_mediacms:setImage');
|
||||||
|
const url = this.currentUrl;
|
||||||
|
if (url === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are any accessibility issues.
|
||||||
|
if (this.updateWarning()) {
|
||||||
|
pendingPromise.resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for invalid width or height.
|
||||||
|
const width = this.root.querySelector(Selectors.IMAGE.elements.width).value;
|
||||||
|
if (!isPercentageValue(width) && isNaN(parseInt(width, 10))) {
|
||||||
|
this.root.querySelector(Selectors.IMAGE.elements.width).focus();
|
||||||
|
pendingPromise.resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const height = this.root.querySelector(Selectors.IMAGE.elements.height).value;
|
||||||
|
if (!isPercentageValue(height) && isNaN(parseInt(height, 10))) {
|
||||||
|
this.root.querySelector(Selectors.IMAGE.elements.height).focus();
|
||||||
|
pendingPromise.resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Templates.render('tiny_mediacms/image', this.getImageContext())
|
||||||
|
.then((html) => {
|
||||||
|
this.editor.insertContent(html);
|
||||||
|
this.currentModal.destroy();
|
||||||
|
pendingPromise.resolve();
|
||||||
|
|
||||||
|
return html;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the image after confirming with the user and loads the insert image page.
|
||||||
|
*/
|
||||||
|
deleteImage() {
|
||||||
|
Notification.deleteCancelPromise(
|
||||||
|
getString('deleteimage', 'tiny_mediacms'),
|
||||||
|
getString('deleteimagewarning', 'tiny_mediacms'),
|
||||||
|
).then(() => {
|
||||||
|
hideElements(Selectors.IMAGE.elements.altWarning, this.root);
|
||||||
|
// Removing the image in the preview will bring the user to the insert page.
|
||||||
|
this.loadInsertImage();
|
||||||
|
return;
|
||||||
|
}).catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEventListeners() {
|
||||||
|
const submitAction = this.root.querySelector(Selectors.IMAGE.actions.submit);
|
||||||
|
submitAction.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.setImage();
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteImageEle = this.root.querySelector(Selectors.IMAGE.actions.deleteImage);
|
||||||
|
deleteImageEle.addEventListener('click', () => {
|
||||||
|
this.deleteImage();
|
||||||
|
});
|
||||||
|
deleteImageEle.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
this.deleteImage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.root.addEventListener('change', (e) => {
|
||||||
|
const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);
|
||||||
|
if (presentationEle) {
|
||||||
|
this.presentationChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);
|
||||||
|
if (constrainEle) {
|
||||||
|
this.autoAdjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
const sizeOriginalEle = e.target.closest(Selectors.IMAGE.elements.sizeOriginal);
|
||||||
|
if (sizeOriginalEle) {
|
||||||
|
this.sizeChecked('original');
|
||||||
|
}
|
||||||
|
|
||||||
|
const sizeCustomEle = e.target.closest(Selectors.IMAGE.elements.sizeCustom);
|
||||||
|
if (sizeCustomEle) {
|
||||||
|
this.sizeChecked('custom');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.root.addEventListener('blur', (e) => {
|
||||||
|
if (e.target.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
|
||||||
|
const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);
|
||||||
|
if (presentationEle) {
|
||||||
|
this.presentationChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
// Character count.
|
||||||
|
this.root.addEventListener('keyup', (e) => {
|
||||||
|
const altEle = e.target.closest(Selectors.IMAGE.elements.alt);
|
||||||
|
if (altEle) {
|
||||||
|
this.handleKeyupCharacterCount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.root.addEventListener('input', (e) => {
|
||||||
|
const widthEle = e.target.closest(Selectors.IMAGE.elements.width);
|
||||||
|
if (widthEle) {
|
||||||
|
// Avoid empty value.
|
||||||
|
widthEle.value = widthEle.value === "" ? 0 : Number(widthEle.value);
|
||||||
|
this.autoAdjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
const heightEle = e.target.closest(Selectors.IMAGE.elements.height);
|
||||||
|
if (heightEle) {
|
||||||
|
// Avoid empty value.
|
||||||
|
heightEle.value = heightEle.value === "" ? 0 : Number(heightEle.value);
|
||||||
|
this.autoAdjustSize(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyupCharacterCount() {
|
||||||
|
const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;
|
||||||
|
const current = this.root.querySelector('#currentcount');
|
||||||
|
current.innerHTML = alt.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the dimensions to fit a square into a specified box while maintaining aspect ratio.
|
||||||
|
*
|
||||||
|
* @param {number} squareWidth - The width of the square.
|
||||||
|
* @param {number} squareHeight - The height of the square.
|
||||||
|
* @param {number} boxWidth - The width of the box.
|
||||||
|
* @param {number} boxHeight - The height of the box.
|
||||||
|
* @returns {Object} An object with the new width and height of the square to fit in the box.
|
||||||
|
*/
|
||||||
|
fitSquareIntoBox = (squareWidth, squareHeight, boxWidth, boxHeight) => {
|
||||||
|
if (squareWidth < boxWidth && squareHeight < boxHeight) {
|
||||||
|
// If the square is smaller than the box, keep its dimensions.
|
||||||
|
return {
|
||||||
|
width: squareWidth,
|
||||||
|
height: squareHeight,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Calculate the scaling factor based on the minimum scaling required to fit in the box.
|
||||||
|
const widthScaleFactor = boxWidth / squareWidth;
|
||||||
|
const heightScaleFactor = boxHeight / squareHeight;
|
||||||
|
const minScaleFactor = Math.min(widthScaleFactor, heightScaleFactor);
|
||||||
|
// Scale the square's dimensions based on the aspect ratio and the minimum scaling factor.
|
||||||
|
const newWidth = squareWidth * minScaleFactor;
|
||||||
|
const newHeight = squareHeight * minScaleFactor;
|
||||||
|
return {
|
||||||
|
width: newWidth,
|
||||||
|
height: newHeight,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
149
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagehelpers.js
Executable file
149
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagehelpers.js
Executable file
@@ -0,0 +1,149 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny media plugin image helpers.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/imagehelpers
|
||||||
|
* @copyright 2024 Meirza <meirza.arson@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Templates from 'core/templates';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders and inserts the body template for inserting an image into the modal.
|
||||||
|
*
|
||||||
|
* @param {object} templateContext - The context for rendering the template.
|
||||||
|
* @param {HTMLElement} root - The root element where the template will be inserted.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export const bodyImageInsert = async(templateContext, root) => {
|
||||||
|
return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert', {...templateContext})
|
||||||
|
.then(({html, js}) => {
|
||||||
|
Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders and inserts the footer template for inserting an image into the modal.
|
||||||
|
*
|
||||||
|
* @param {object} templateContext - The context for rendering the template.
|
||||||
|
* @param {HTMLElement} root - The root element where the template will be inserted.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export const footerImageInsert = async(templateContext, root) => {
|
||||||
|
return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert_footer', {...templateContext})
|
||||||
|
.then(({html, js}) => {
|
||||||
|
Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders and inserts the body template for displaying image details in the modal.
|
||||||
|
*
|
||||||
|
* @param {object} templateContext - The context for rendering the template.
|
||||||
|
* @param {HTMLElement} root - The root element where the template will be inserted.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export const bodyImageDetails = async(templateContext, root) => {
|
||||||
|
return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details', {...templateContext})
|
||||||
|
.then(({html, js}) => {
|
||||||
|
Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders and inserts the footer template for displaying image details in the modal.
|
||||||
|
* @param {object} templateContext - The context for rendering the template.
|
||||||
|
* @param {HTMLElement} root - The root element where the template will be inserted.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export const footerImageDetails = async(templateContext, root) => {
|
||||||
|
return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details_footer', {...templateContext})
|
||||||
|
.then(({html, js}) => {
|
||||||
|
Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js);
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the element(s).
|
||||||
|
*
|
||||||
|
* @param {string|string[]} elements - The CSS selector for the elements to toggle.
|
||||||
|
* @param {object} root - The CSS selector for the elements to toggle.
|
||||||
|
*/
|
||||||
|
export const showElements = (elements, root) => {
|
||||||
|
if (elements instanceof Array) {
|
||||||
|
elements.forEach((elementSelector) => {
|
||||||
|
const element = root.querySelector(elementSelector);
|
||||||
|
if (element) {
|
||||||
|
element.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const element = root.querySelector(elements);
|
||||||
|
if (element) {
|
||||||
|
element.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the element(s).
|
||||||
|
*
|
||||||
|
* @param {string|string[]} elements - The CSS selector for the elements to toggle.
|
||||||
|
* @param {object} root - The CSS selector for the elements to toggle.
|
||||||
|
*/
|
||||||
|
export const hideElements = (elements, root) => {
|
||||||
|
if (elements instanceof Array) {
|
||||||
|
elements.forEach((elementSelector) => {
|
||||||
|
const element = root.querySelector(elementSelector);
|
||||||
|
if (element) {
|
||||||
|
element.classList.add('d-none');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const element = root.querySelector(elements);
|
||||||
|
if (element) {
|
||||||
|
element.classList.add('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given value is a percentage value.
|
||||||
|
*
|
||||||
|
* @param {string} value - The value to check.
|
||||||
|
* @returns {boolean} True if the value is a percentage value, false otherwise.
|
||||||
|
*/
|
||||||
|
export const isPercentageValue = (value) => {
|
||||||
|
return value.match(/\d+%/);
|
||||||
|
};
|
||||||
282
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imageinsert.js
Executable file
282
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imageinsert.js
Executable file
@@ -0,0 +1,282 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny media plugin image insertion class for Moodle.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/imageinsert
|
||||||
|
* @copyright 2024 Meirza <meirza.arson@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Selectors from './selectors';
|
||||||
|
import Dropzone from 'core/dropzone';
|
||||||
|
import uploadFile from 'editor_tiny/uploader';
|
||||||
|
import {prefetchStrings} from 'core/prefetch';
|
||||||
|
import {getStrings} from 'core/str';
|
||||||
|
import {component} from "./common";
|
||||||
|
import {getFilePicker} from 'editor_tiny/options';
|
||||||
|
import {displayFilepicker} from 'editor_tiny/utils';
|
||||||
|
import {ImageDetails} from 'tiny_mediacms/imagedetails';
|
||||||
|
import {
|
||||||
|
showElements,
|
||||||
|
hideElements,
|
||||||
|
bodyImageDetails,
|
||||||
|
footerImageDetails,
|
||||||
|
} from 'tiny_mediacms/imagehelpers';
|
||||||
|
|
||||||
|
prefetchStrings('tiny_mediacms', [
|
||||||
|
'insertimage',
|
||||||
|
'enterurl',
|
||||||
|
'enterurlor',
|
||||||
|
'imageurlrequired',
|
||||||
|
'uploading',
|
||||||
|
'loading',
|
||||||
|
'addfilesdrop',
|
||||||
|
'sizecustom_help',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export class ImageInsert {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
root,
|
||||||
|
editor,
|
||||||
|
currentModal,
|
||||||
|
canShowFilePicker,
|
||||||
|
canShowDropZone,
|
||||||
|
) {
|
||||||
|
this.root = root;
|
||||||
|
this.editor = editor;
|
||||||
|
this.currentModal = currentModal;
|
||||||
|
this.canShowFilePicker = canShowFilePicker;
|
||||||
|
this.canShowDropZone = canShowDropZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
init = async function() {
|
||||||
|
// Get the localization lang strings and turn them into object.
|
||||||
|
const langStringKeys = [
|
||||||
|
'insertimage',
|
||||||
|
'enterurl',
|
||||||
|
'enterurlor',
|
||||||
|
'imageurlrequired',
|
||||||
|
'uploading',
|
||||||
|
'loading',
|
||||||
|
'addfilesdrop',
|
||||||
|
'sizecustom_help',
|
||||||
|
];
|
||||||
|
const langStringvalues = await getStrings([...langStringKeys].map((key) => ({key, component})));
|
||||||
|
|
||||||
|
// Convert array to object.
|
||||||
|
this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]]));
|
||||||
|
this.currentModal.setTitle(this.langStrings.insertimage);
|
||||||
|
if (this.canShowDropZone) {
|
||||||
|
const dropZoneEle = document.querySelector(Selectors.IMAGE.elements.dropzoneContainer);
|
||||||
|
|
||||||
|
// Accepted types can be either a string or an array.
|
||||||
|
let acceptedTypes = getFilePicker(this.editor, 'image').accepted_types;
|
||||||
|
if (Array.isArray(acceptedTypes)) {
|
||||||
|
acceptedTypes = acceptedTypes.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
const dropZone = new Dropzone(
|
||||||
|
dropZoneEle,
|
||||||
|
acceptedTypes,
|
||||||
|
files => {
|
||||||
|
this.handleUploadedFile(files);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
dropZone.setLabel(this.langStrings.addfilesdrop);
|
||||||
|
dropZone.init();
|
||||||
|
}
|
||||||
|
await this.registerEventListeners();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables the URL-related buttons in the footer based on the current URL and input value.
|
||||||
|
*/
|
||||||
|
toggleUrlButton() {
|
||||||
|
const urlInput = this.root.querySelector(Selectors.IMAGE.elements.url);
|
||||||
|
const url = urlInput.value;
|
||||||
|
const addUrl = this.root.querySelector(Selectors.IMAGE.actions.addUrl);
|
||||||
|
addUrl.disabled = !(url !== "" && this.isValidUrl(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if given string is a valid URL.
|
||||||
|
*
|
||||||
|
* @param {String} urlString URL the link will point to.
|
||||||
|
* @returns {boolean} True is valid, otherwise false.
|
||||||
|
*/
|
||||||
|
isValidUrl = urlString => {
|
||||||
|
const urlPattern = new RegExp('^(https?:\\/\\/)?' + // Protocol.
|
||||||
|
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // Domain name.
|
||||||
|
'((\\d{1,3}\\.){3}\\d{1,3})|localhost)' + // OR ip (v4) address, localhost.
|
||||||
|
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'); // Port and path.
|
||||||
|
return !!urlPattern.test(urlString);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles changes in the image URL input field and loads a preview of the image if the URL has changed.
|
||||||
|
*/
|
||||||
|
urlChanged() {
|
||||||
|
hideElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||||
|
const input = this.root.querySelector(Selectors.IMAGE.elements.url);
|
||||||
|
if (input.value && input.value !== this.currentUrl) {
|
||||||
|
this.loadPreviewImage(input.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads and displays a preview image based on the provided URL, and handles image loading events.
|
||||||
|
*
|
||||||
|
* @param {string} url - The URL of the image to load and display.
|
||||||
|
*/
|
||||||
|
loadPreviewImage = function(url) {
|
||||||
|
this.startImageLoading();
|
||||||
|
this.currentUrl = url;
|
||||||
|
const image = new Image();
|
||||||
|
image.src = url;
|
||||||
|
image.addEventListener('error', () => {
|
||||||
|
const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);
|
||||||
|
urlWarningLabelEle.innerHTML = this.langStrings.imageurlrequired;
|
||||||
|
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||||
|
this.currentUrl = "";
|
||||||
|
this.stopImageLoading();
|
||||||
|
});
|
||||||
|
|
||||||
|
image.addEventListener('load', () => {
|
||||||
|
let templateContext = {};
|
||||||
|
templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help};
|
||||||
|
Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])
|
||||||
|
.then(() => {
|
||||||
|
const imagedetails = new ImageDetails(
|
||||||
|
this.root,
|
||||||
|
this.editor,
|
||||||
|
this.currentModal,
|
||||||
|
this.canShowFilePicker,
|
||||||
|
this.canShowDropZone,
|
||||||
|
this.currentUrl,
|
||||||
|
image,
|
||||||
|
);
|
||||||
|
imagedetails.init();
|
||||||
|
return;
|
||||||
|
}).then(() => {
|
||||||
|
this.stopImageLoading();
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the upload loader and disables UI elements while loading a file.
|
||||||
|
*/
|
||||||
|
startImageLoading() {
|
||||||
|
showElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
||||||
|
const elementsToHide = [
|
||||||
|
Selectors.IMAGE.elements.insertImage,
|
||||||
|
Selectors.IMAGE.elements.urlWarning,
|
||||||
|
Selectors.IMAGE.elements.modalFooter,
|
||||||
|
];
|
||||||
|
hideElements(elementsToHide, this.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the upload loader and disables UI elements while loading a file.
|
||||||
|
*/
|
||||||
|
stopImageLoading() {
|
||||||
|
hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
||||||
|
const elementsToShow = [
|
||||||
|
Selectors.IMAGE.elements.insertImage,
|
||||||
|
Selectors.IMAGE.elements.modalFooter,
|
||||||
|
];
|
||||||
|
showElements(elementsToShow, this.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
filePickerCallback(params) {
|
||||||
|
if (params.url) {
|
||||||
|
this.loadPreviewImage(params.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the content of the loader icon.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} root - The root element containing the loader icon.
|
||||||
|
* @param {object} langStrings - An object containing language strings.
|
||||||
|
* @param {number|null} progress - The progress percentage (optional).
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
updateLoaderIcon = (root, langStrings, progress = null) => {
|
||||||
|
const loaderIcon = root.querySelector(Selectors.IMAGE.elements.loaderIconContainer + ' div');
|
||||||
|
loaderIcon.innerHTML = progress !== null ? `${langStrings.uploading} ${Math.round(progress)}%` : langStrings.loading;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the uploaded file, initiates the upload process, and updates the UI during the upload.
|
||||||
|
*
|
||||||
|
* @param {FileList} files - The list of files to upload (usually from a file input field).
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the file is uploaded and processed.
|
||||||
|
*/
|
||||||
|
handleUploadedFile = async(files) => {
|
||||||
|
try {
|
||||||
|
this.startImageLoading();
|
||||||
|
const fileURL = await uploadFile(this.editor, 'image', files[0], files[0].name, (progress) => {
|
||||||
|
this.updateLoaderIcon(this.root, this.langStrings, progress);
|
||||||
|
});
|
||||||
|
// Set the loader icon content to "loading" after the file upload completes.
|
||||||
|
this.updateLoaderIcon(this.root, this.langStrings);
|
||||||
|
this.filePickerCallback({url: fileURL});
|
||||||
|
} catch (error) {
|
||||||
|
// Handle the error.
|
||||||
|
const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);
|
||||||
|
urlWarningLabelEle.innerHTML = error.error !== undefined ? error.error : error;
|
||||||
|
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||||
|
this.stopImageLoading();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEventListeners() {
|
||||||
|
this.root.addEventListener('click', async(e) => {
|
||||||
|
const addUrlEle = e.target.closest(Selectors.IMAGE.actions.addUrl);
|
||||||
|
if (addUrlEle) {
|
||||||
|
this.urlChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);
|
||||||
|
if (imageBrowserAction && this.canShowFilePicker) {
|
||||||
|
e.preventDefault();
|
||||||
|
const params = await displayFilepicker(this.editor, 'image');
|
||||||
|
this.filePickerCallback(params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.root.addEventListener('input', (e) => {
|
||||||
|
const urlEle = e.target.closest(Selectors.IMAGE.elements.url);
|
||||||
|
if (urlEle) {
|
||||||
|
this.toggleUrlButton();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileInput = this.root.querySelector(Selectors.IMAGE.elements.fileInput);
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.addEventListener('change', () => {
|
||||||
|
this.handleUploadedFile(fileInput.files);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagemodal.js
Executable file
49
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagemodal.js
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image Modal for Tiny.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/imagemodal
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Modal from 'core/modal';
|
||||||
|
import {component} from './common';
|
||||||
|
|
||||||
|
export default class ImageModal extends Modal {
|
||||||
|
static TYPE = `${component}/imagemodal`;
|
||||||
|
static TEMPLATE = `${component}/insert_image_modal`;
|
||||||
|
|
||||||
|
registerEventListeners() {
|
||||||
|
// Call the parent registration.
|
||||||
|
super.registerEventListeners();
|
||||||
|
|
||||||
|
// Register to close on save/cancel.
|
||||||
|
this.registerCloseOnSave();
|
||||||
|
this.registerCloseOnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(modalConfig) {
|
||||||
|
modalConfig.large = true;
|
||||||
|
modalConfig.removeOnClose = true;
|
||||||
|
modalConfig.show = true;
|
||||||
|
|
||||||
|
super.configure(modalConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageModal.registerModalType();
|
||||||
86
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/manager.js
Executable file
86
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/manager.js
Executable file
@@ -0,0 +1,86 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny Media Manager plugin class for Moodle.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/manager
|
||||||
|
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Templates from 'core/templates';
|
||||||
|
import {getString} from 'core/str';
|
||||||
|
import Modal from 'core/modal';
|
||||||
|
import * as ModalEvents from 'core/modal_events';
|
||||||
|
import {getData} from './options';
|
||||||
|
import Config from 'core/config';
|
||||||
|
|
||||||
|
export default class MediaManager {
|
||||||
|
|
||||||
|
editor = null;
|
||||||
|
area = null;
|
||||||
|
|
||||||
|
constructor(editor) {
|
||||||
|
this.editor = editor;
|
||||||
|
const data = getData(editor);
|
||||||
|
this.area = data.params.area;
|
||||||
|
this.area.itemid = data.fpoptions.image.itemid;
|
||||||
|
}
|
||||||
|
|
||||||
|
async displayDialogue() {
|
||||||
|
const modal = await Modal.create({
|
||||||
|
large: true,
|
||||||
|
title: getString('mediamanagerproperties', 'tiny_mediacms'),
|
||||||
|
body: Templates.render('tiny_mediacms/mm2_iframe', {
|
||||||
|
src: this.getIframeURL()
|
||||||
|
}),
|
||||||
|
removeOnClose: true,
|
||||||
|
show: true,
|
||||||
|
});
|
||||||
|
modal.getRoot().on(ModalEvents.bodyRendered, () => {
|
||||||
|
this.selectFirstElement();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('.modal-lg').style.cssText = `max-width: 850px`;
|
||||||
|
return modal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It will select the first element in the file manager.
|
||||||
|
selectFirstElement() {
|
||||||
|
const iframe = document.getElementById('mm2-iframe');
|
||||||
|
iframe.addEventListener('load', function() {
|
||||||
|
let intervalId = setInterval(function() {
|
||||||
|
const iDocument = iframe.contentWindow.document;
|
||||||
|
if (iDocument.querySelector('.filemanager')) {
|
||||||
|
const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])');
|
||||||
|
if (firstFocusableElement) {
|
||||||
|
firstFocusableElement.focus();
|
||||||
|
}
|
||||||
|
clearInterval(intervalId);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getIframeURL() {
|
||||||
|
const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/mediacms/manage.php`);
|
||||||
|
url.searchParams.append('elementid', this.editor.getElement().id);
|
||||||
|
for (const key in this.area) {
|
||||||
|
url.searchParams.append(key, this.area[key]);
|
||||||
|
}
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
117
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/options.js
Executable file
117
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/options.js
Executable file
@@ -0,0 +1,117 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options helper for Tiny Media plugin.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/options
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {getPluginOptionName} from 'editor_tiny/options';
|
||||||
|
import {pluginName} from './common';
|
||||||
|
|
||||||
|
const dataName = getPluginOptionName(pluginName, 'data');
|
||||||
|
const permissionsName = getPluginOptionName(pluginName, 'permissions');
|
||||||
|
const ltiName = getPluginOptionName(pluginName, 'lti');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the options for the Tiny Media plugin.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor
|
||||||
|
*/
|
||||||
|
export const register = (editor) => {
|
||||||
|
const registerOption = editor.options.register;
|
||||||
|
|
||||||
|
registerOption(permissionsName, {
|
||||||
|
processor: 'object',
|
||||||
|
"default": {
|
||||||
|
image: {
|
||||||
|
filepicker: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registerOption(dataName, {
|
||||||
|
processor: 'object',
|
||||||
|
"default": {
|
||||||
|
// MediaCMS video library configuration
|
||||||
|
mediacmsApiUrl: '', // e.g., 'https://deic.mediacms.io/api/v1/media'
|
||||||
|
mediacmsBaseUrl: '', // e.g., 'https://deic.mediacms.io'
|
||||||
|
mediacmsPageSize: 12,
|
||||||
|
// Auto-conversion settings
|
||||||
|
autoConvertEnabled: true, // Enable/disable auto-conversion of pasted MediaCMS URLs
|
||||||
|
autoConvertBaseUrl: '', // Base URL to restrict auto-conversion (empty = allow all MediaCMS domains)
|
||||||
|
autoConvertOptions: {
|
||||||
|
// Default embed options for auto-converted videos
|
||||||
|
showTitle: true,
|
||||||
|
linkTitle: true,
|
||||||
|
showRelated: true,
|
||||||
|
showUserAvatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registerOption(ltiName, {
|
||||||
|
processor: 'object',
|
||||||
|
"default": {
|
||||||
|
// LTI configuration for MediaCMS iframe library
|
||||||
|
toolId: 0, // LTI external tool ID
|
||||||
|
courseId: 0, // Current course ID
|
||||||
|
contentItemUrl: '', // URL to /mod/lti/contentitem.php for Deep Linking
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the permissions configuration for the Tiny Media plugin.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
export const getPermissions = (editor) => editor.options.get(permissionsName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the permissions configuration for the Tiny Media plugin.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
export const getImagePermissions = (editor) => getPermissions(editor).image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the permissions configuration for the Tiny Media plugin.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
export const getEmbedPermissions = (editor) => getPermissions(editor).embed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data configuration for the Media Manager.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
export const getData = (editor) => editor.options.get(dataName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the LTI configuration for the MediaCMS iframe library.
|
||||||
|
*
|
||||||
|
* @param {TinyMCE} editor
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
export const getLti = (editor) => editor.options.get(ltiName);
|
||||||
92
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/plugin.js
Executable file
92
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/plugin.js
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny Media plugin for Moodle.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/plugin
|
||||||
|
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
import {getTinyMCE} from 'editor_tiny/loader';
|
||||||
|
import {getPluginMetadata} from 'editor_tiny/utils';
|
||||||
|
|
||||||
|
import {component, pluginName} from './common';
|
||||||
|
import * as Commands from './commands';
|
||||||
|
import * as Configuration from './configuration';
|
||||||
|
import * as Options from './options';
|
||||||
|
import {setupAutoConvert} from './autoconvert';
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-async-promise-executor
|
||||||
|
export default new Promise(async(resolve) => {
|
||||||
|
const [
|
||||||
|
tinyMCE,
|
||||||
|
setupCommands,
|
||||||
|
pluginMetadata,
|
||||||
|
] = await Promise.all([
|
||||||
|
getTinyMCE(),
|
||||||
|
Commands.getSetup(),
|
||||||
|
getPluginMetadata(component, pluginName),
|
||||||
|
]);
|
||||||
|
|
||||||
|
tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {
|
||||||
|
// Register options.
|
||||||
|
Options.register(editor);
|
||||||
|
|
||||||
|
// Setup the Commands (buttons, menu items, and so on).
|
||||||
|
setupCommands(editor);
|
||||||
|
|
||||||
|
// Setup auto-conversion of pasted MediaCMS URLs.
|
||||||
|
setupAutoConvert(editor);
|
||||||
|
|
||||||
|
// Clean up editor-only elements before content is saved.
|
||||||
|
// Remove wrapper divs and edit buttons that are only for the editor UI.
|
||||||
|
editor.on('GetContent', (e) => {
|
||||||
|
if (e.format === 'html') {
|
||||||
|
// Create a temporary container to manipulate the HTML
|
||||||
|
const tempDiv = document.createElement('div');
|
||||||
|
tempDiv.innerHTML = e.content;
|
||||||
|
|
||||||
|
// Remove edit buttons
|
||||||
|
tempDiv.querySelectorAll('.tiny-mediacms-edit-btn').forEach(btn => btn.remove());
|
||||||
|
|
||||||
|
// Unwrap iframes from tiny-mediacms-iframe-wrapper
|
||||||
|
tempDiv.querySelectorAll('.tiny-mediacms-iframe-wrapper').forEach(wrapper => {
|
||||||
|
const iframe = wrapper.querySelector('iframe');
|
||||||
|
if (iframe) {
|
||||||
|
wrapper.parentNode.insertBefore(iframe, wrapper);
|
||||||
|
}
|
||||||
|
wrapper.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Unwrap iframes from tiny-iframe-responsive
|
||||||
|
tempDiv.querySelectorAll('.tiny-iframe-responsive').forEach(wrapper => {
|
||||||
|
const iframe = wrapper.querySelector('iframe');
|
||||||
|
if (iframe) {
|
||||||
|
wrapper.parentNode.insertBefore(iframe, wrapper);
|
||||||
|
}
|
||||||
|
wrapper.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
e.content = tempDiv.innerHTML;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return pluginMetadata;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Resolve the Media Plugin and include configuration.
|
||||||
|
resolve([`${component}/plugin`, Configuration]);
|
||||||
|
});
|
||||||
162
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/selectors.js
Executable file
162
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/selectors.js
Executable file
@@ -0,0 +1,162 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny Media plugin helper function to build queryable data selectors.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/selectors
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
IMAGE: {
|
||||||
|
actions: {
|
||||||
|
submit: '.tiny_imagecms_urlentrysubmit',
|
||||||
|
imageBrowser: '.openimagecmsbrowser',
|
||||||
|
addUrl: '.tiny_imagecms_addurl',
|
||||||
|
deleteImage: '.tiny_imagecms_deleteicon',
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
form: 'form.tiny_imagecms_form',
|
||||||
|
alignSettings: '.tiny_imagecms_button',
|
||||||
|
alt: '.tiny_imagecms_altentry',
|
||||||
|
altWarning: '.tiny_imagecms_altwarning',
|
||||||
|
height: '.tiny_imagecms_heightentry',
|
||||||
|
width: '.tiny_imagecms_widthentry',
|
||||||
|
url: '.tiny_imagecms_urlentry',
|
||||||
|
urlWarning: '.tiny_imagecms_urlwarning',
|
||||||
|
size: '.tiny_imagecms_size',
|
||||||
|
presentation: '.tiny_imagecms_presentation',
|
||||||
|
constrain: '.tiny_imagecms_constrain',
|
||||||
|
customStyle: '.tiny_imagecms_customstyle',
|
||||||
|
preview: '.tiny_imagecms_preview',
|
||||||
|
previewBox: '.tiny_imagecms_preview_box',
|
||||||
|
loaderIcon: '.tiny_imagecms_loader',
|
||||||
|
loaderIconContainer: '.tiny_imagecms_loader_container',
|
||||||
|
insertImage: '.tiny_imagecms_insert_image',
|
||||||
|
modalFooter: '.modal-footer',
|
||||||
|
dropzoneContainer: '.tiny_imagecms_dropzone_container',
|
||||||
|
fileInput: '#tiny_imagecms_fileinput',
|
||||||
|
fileNameLabel: '.tiny_imagecms_filename',
|
||||||
|
sizeOriginal: '.tiny_imagecms_sizeoriginal',
|
||||||
|
sizeCustom: '.tiny_imagecms_sizecustom',
|
||||||
|
properties: '.tiny_imagecms_properties',
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
responsive: 'img-fluid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EMBED: {
|
||||||
|
actions: {
|
||||||
|
submit: '.tiny_mediacms_submit',
|
||||||
|
mediaBrowser: '.openmediacmsbrowser',
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
form: 'form.tiny_mediacms_form',
|
||||||
|
source: '.tiny_mediacms_source',
|
||||||
|
track: '.tiny_mediacms_track',
|
||||||
|
mediaSource: '.tiny_mediacms_media_source',
|
||||||
|
linkSource: '.tiny_mediacms_link_source',
|
||||||
|
linkSize: '.tiny_mediacms_link_size',
|
||||||
|
posterSource: '.tiny_mediacms_poster_source',
|
||||||
|
posterSize: '.tiny_mediacms_poster_size',
|
||||||
|
displayOptions: '.tiny_mediacms_display_options',
|
||||||
|
name: '.tiny_mediacms_name_entry',
|
||||||
|
title: '.tiny_mediacms_title_entry',
|
||||||
|
url: '.tiny_mediacms_url_entry',
|
||||||
|
width: '.tiny_mediacms_width_entry',
|
||||||
|
height: '.tiny_mediacms_height_entry',
|
||||||
|
trackSource: '.tiny_mediacms_track_source',
|
||||||
|
trackKind: '.tiny_mediacms_track_kind_entry',
|
||||||
|
trackLabel: '.tiny_mediacms_track_label_entry',
|
||||||
|
trackLang: '.tiny_mediacms_track_lang_entry',
|
||||||
|
trackDefault: '.tiny_mediacms_track_default',
|
||||||
|
mediaControl: '.tiny_mediacms_controls',
|
||||||
|
mediaAutoplay: '.tiny_mediacms_autoplay',
|
||||||
|
mediaMute: '.tiny_mediacms_mute',
|
||||||
|
mediaLoop: '.tiny_mediacms_loop',
|
||||||
|
advancedSettings: '.tiny_mediacms_advancedsettings',
|
||||||
|
linkTab: 'li[data-medium-type="link"]',
|
||||||
|
videoTab: 'li[data-medium-type="video"]',
|
||||||
|
audioTab: 'li[data-medium-type="audio"]',
|
||||||
|
linkPane: '.tab-pane[data-medium-type="link"]',
|
||||||
|
videoPane: '.tab-pane[data-medium-type="video"]',
|
||||||
|
audioPane: '.tab-pane[data-medium-type="audio"]',
|
||||||
|
trackSubtitlesTab: 'li[data-track-kind="subtitles"]',
|
||||||
|
trackCaptionsTab: 'li[data-track-kind="captions"]',
|
||||||
|
trackDescriptionsTab: 'li[data-track-kind="descriptions"]',
|
||||||
|
trackChaptersTab: 'li[data-track-kind="chapters"]',
|
||||||
|
trackMetadataTab: 'li[data-track-kind="metadata"]',
|
||||||
|
trackSubtitlesPane: '.tab-pane[data-track-kind="subtitles"]',
|
||||||
|
trackCaptionsPane: '.tab-pane[data-track-kind="captions"]',
|
||||||
|
trackDescriptionsPane: '.tab-pane[data-track-kind="descriptions"]',
|
||||||
|
trackChaptersPane: '.tab-pane[data-track-kind="chapters"]',
|
||||||
|
trackMetadataPane: '.tab-pane[data-track-kind="metadata"]',
|
||||||
|
},
|
||||||
|
mediaTypes: {
|
||||||
|
link: 'LINK',
|
||||||
|
video: 'VIDEO',
|
||||||
|
audio: 'AUDIO',
|
||||||
|
},
|
||||||
|
trackKinds: {
|
||||||
|
subtitles: 'SUBTITLES',
|
||||||
|
captions: 'CAPTIONS',
|
||||||
|
descriptions: 'DESCRIPTIONS',
|
||||||
|
chapters: 'CHAPTERS',
|
||||||
|
metadata: 'METADATA',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
IFRAME: {
|
||||||
|
actions: {
|
||||||
|
remove: '[data-action="remove"]',
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
form: 'form.tiny_iframecms_form',
|
||||||
|
url: '.tiny_iframecms_url',
|
||||||
|
urlWarning: '.tiny_iframecms_url_warning',
|
||||||
|
showTitle: '.tiny_iframecms_showtitle',
|
||||||
|
linkTitle: '.tiny_iframecms_linktitle',
|
||||||
|
showRelated: '.tiny_iframecms_showrelated',
|
||||||
|
showUserAvatar: '.tiny_iframecms_showuseravatar',
|
||||||
|
responsive: '.tiny_iframecms_responsive',
|
||||||
|
startAt: '.tiny_iframecms_startat',
|
||||||
|
startAtEnabled: '.tiny_iframecms_startat_enabled',
|
||||||
|
aspectRatio: '.tiny_iframecms_aspectratio',
|
||||||
|
width: '.tiny_iframecms_width',
|
||||||
|
height: '.tiny_iframecms_height',
|
||||||
|
preview: '.tiny_iframecms_preview',
|
||||||
|
previewContainer: '.tiny_iframecms_preview_container',
|
||||||
|
// Tab elements
|
||||||
|
tabs: '.tiny_iframecms_tabs',
|
||||||
|
tabUrlBtn: '.tiny_iframecms_tab_url_btn',
|
||||||
|
tabIframeLibraryBtn: '.tiny_iframecms_tab_iframe_library_btn',
|
||||||
|
paneUrl: '.tiny_iframecms_pane_url',
|
||||||
|
paneIframeLibrary: '.tiny_iframecms_pane_iframe_library',
|
||||||
|
// Iframe library elements
|
||||||
|
iframeLibraryContainer: '.tiny_iframecms_iframe_library_container',
|
||||||
|
iframeLibraryPlaceholder:
|
||||||
|
'.tiny_iframecms_iframe_library_placeholder',
|
||||||
|
iframeLibraryLoading: '.tiny_iframecms_iframe_library_loading',
|
||||||
|
iframeLibraryFrame: '.tiny_iframecms_iframe_library_frame',
|
||||||
|
},
|
||||||
|
aspectRatios: {
|
||||||
|
'16:9': { width: 560, height: 315 },
|
||||||
|
'4:3': { width: 560, height: 420 },
|
||||||
|
'1:1': { width: 400, height: 400 },
|
||||||
|
custom: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
95
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/usedfiles.js
Executable file
95
lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/usedfiles.js
Executable file
@@ -0,0 +1,95 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny Media Manager usedfiles.
|
||||||
|
*
|
||||||
|
* @module tiny_mediacms/usedfiles
|
||||||
|
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Templates from 'core/templates';
|
||||||
|
import Config from 'core/config';
|
||||||
|
|
||||||
|
class UsedFileManager {
|
||||||
|
constructor(files, userContext, itemId, elementId) {
|
||||||
|
this.files = files;
|
||||||
|
this.userContext = userContext;
|
||||||
|
this.itemId = itemId;
|
||||||
|
this.elementId = elementId;
|
||||||
|
}
|
||||||
|
|
||||||
|
getElementId() {
|
||||||
|
return this.elementId;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUsedFiles() {
|
||||||
|
const editor = window.parent.tinymce.EditorManager.get(this.getElementId());
|
||||||
|
if (!editor) {
|
||||||
|
window.console.error(`Editor not found for ${this.getElementId()}`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const content = editor.getContent();
|
||||||
|
const baseUrl = `${Config.wwwroot}/draftfile.php/${this.userContext}/user/draft/${this.itemId}/`;
|
||||||
|
const pattern = new RegExp("[\"']" + baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + "(?<filename>.+?)[\\?\"']", 'gm');
|
||||||
|
|
||||||
|
const usedFiles = [...content.matchAll(pattern)].map((match) => decodeURIComponent(match.groups.filename));
|
||||||
|
|
||||||
|
return usedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an array of unused files.
|
||||||
|
findUnusedFiles(usedFiles) {
|
||||||
|
return Object.entries(this.files)
|
||||||
|
.filter(([filename]) => !usedFiles.includes(filename))
|
||||||
|
.map(([filename]) => filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an array of missing files.
|
||||||
|
findMissingFiles(usedFiles) {
|
||||||
|
return usedFiles.filter((filename) => !this.files.hasOwnProperty(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFiles() {
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
const usedFiles = this.getUsedFiles();
|
||||||
|
const unusedFiles = this.findUnusedFiles(usedFiles);
|
||||||
|
const missingFiles = this.findMissingFiles(usedFiles);
|
||||||
|
|
||||||
|
form.querySelectorAll('input[type=checkbox][name^="deletefile"]').forEach((checkbox) => {
|
||||||
|
if (!unusedFiles.includes(checkbox.dataset.filename)) {
|
||||||
|
checkbox.closest('.fitem').remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
form.classList.toggle('has-missing-files', !!missingFiles.length);
|
||||||
|
form.classList.toggle('has-unused-files', !!unusedFiles.length);
|
||||||
|
|
||||||
|
return Templates.renderForPromise('tiny_mediacms/missingfiles', {
|
||||||
|
missingFiles,
|
||||||
|
}).then(({html, js}) => {
|
||||||
|
Templates.replaceNodeContents(form.querySelector('.missing-files'), html, js);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const init = (files, usercontext, itemid, elementid) => {
|
||||||
|
const manager = new UsedFileManager(files, usercontext, itemid, elementid);
|
||||||
|
manager.updateFiles();
|
||||||
|
|
||||||
|
return manager;
|
||||||
|
};
|
||||||
118
lms-plugins/mediacms-moodle/tiny/mediacms/classes/form/manage_files_form.php
Executable file
118
lms-plugins/mediacms-moodle/tiny/mediacms/classes/form/manage_files_form.php
Executable file
@@ -0,0 +1,118 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atto text editor manage files plugin form.
|
||||||
|
*
|
||||||
|
* @package tiny_mediacms
|
||||||
|
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace tiny_mediacms\form;
|
||||||
|
|
||||||
|
use html_writer;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
require_once("{$CFG->libdir}/formslib.php");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form allowing to edit files in one draft area.
|
||||||
|
*
|
||||||
|
* @package tiny_mediacms
|
||||||
|
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class manage_files_form extends \moodleform {
|
||||||
|
|
||||||
|
public function definition() {
|
||||||
|
global $PAGE, $USER;
|
||||||
|
$mform = $this->_form;
|
||||||
|
|
||||||
|
$mform->setDisableShortforms(true);
|
||||||
|
|
||||||
|
$itemid = $this->_customdata['draftitemid'];
|
||||||
|
$elementid = $this->_customdata['elementid'];
|
||||||
|
$options = $this->_customdata['options'];
|
||||||
|
$files = $this->_customdata['files'];
|
||||||
|
$usercontext = $this->_customdata['context'];
|
||||||
|
$removeorphaneddrafts = $this->_customdata['removeorphaneddrafts'] ?? false;
|
||||||
|
|
||||||
|
$mform->addElement('header', 'filemanagerhdr', get_string('filemanager', 'tiny_mediacms'));
|
||||||
|
|
||||||
|
$mform->addElement('hidden', 'itemid');
|
||||||
|
$mform->setType('itemid', PARAM_INT);
|
||||||
|
|
||||||
|
$mform->addElement('hidden', 'maxbytes');
|
||||||
|
$mform->setType('maxbytes', PARAM_INT);
|
||||||
|
|
||||||
|
$mform->addElement('hidden', 'subdirs');
|
||||||
|
$mform->setType('subdirs', PARAM_INT);
|
||||||
|
|
||||||
|
$mform->addElement('hidden', 'accepted_types');
|
||||||
|
$mform->setType('accepted_types', PARAM_RAW);
|
||||||
|
|
||||||
|
$mform->addElement('hidden', 'return_types');
|
||||||
|
$mform->setType('return_types', PARAM_INT);
|
||||||
|
|
||||||
|
$mform->addElement('hidden', 'context');
|
||||||
|
$mform->setType('context', PARAM_INT);
|
||||||
|
|
||||||
|
$mform->addElement('hidden', 'areamaxbytes');
|
||||||
|
$mform->setType('areamaxbytes', PARAM_INT);
|
||||||
|
|
||||||
|
$mform->addElement('hidden', 'elementid');
|
||||||
|
$mform->setType('elementid', PARAM_TEXT);
|
||||||
|
|
||||||
|
$mform->addElement('filemanager', 'files_filemanager', '', null, $options);
|
||||||
|
|
||||||
|
// Let the user know that any drafts not referenced in the text will be removed automatically.
|
||||||
|
if ($removeorphaneddrafts) {
|
||||||
|
$mform->addElement('static', '', '', html_writer::tag(
|
||||||
|
'div',
|
||||||
|
get_string('unusedfilesremovalnotice', 'tiny_mediacms')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$mform->addElement('header', 'missingfileshdr', get_string('missingfiles', 'tiny_mediacms'));
|
||||||
|
$mform->addElement('static', '', '',
|
||||||
|
html_writer::tag(
|
||||||
|
'div',
|
||||||
|
html_writer::tag('div', get_string('hasmissingfiles', 'tiny_mediacms')) .
|
||||||
|
html_writer::tag('div', '', ['class' => 'missing-files']
|
||||||
|
),
|
||||||
|
['class' => 'file-status'])
|
||||||
|
);
|
||||||
|
|
||||||
|
$mform->addElement('header', 'deletefileshdr', get_string('unusedfilesheader', 'tiny_mediacms'));
|
||||||
|
$mform->addElement('static', '', '', html_writer::tag('div', get_string('unusedfilesdesc', 'tiny_mediacms')));
|
||||||
|
|
||||||
|
foreach ($files as $hash => $file) {
|
||||||
|
$mform->addElement('checkbox', "deletefile[{$hash}]", '', $file, ['data-filename' => $file]);
|
||||||
|
$mform->setType("deletefile[{$hash}]", PARAM_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mform->addElement('submit', 'delete', get_string('deleteselected', 'tiny_mediacms'));
|
||||||
|
|
||||||
|
$PAGE->requires->js_call_amd('tiny_mediacms/usedfiles', 'init', [
|
||||||
|
'files' => array_flip($files),
|
||||||
|
'usercontext' => $usercontext->id,
|
||||||
|
'itemid' => $itemid,
|
||||||
|
'elementid' => $elementid,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
224
lms-plugins/mediacms-moodle/tiny/mediacms/classes/plugininfo.php
Executable file
224
lms-plugins/mediacms-moodle/tiny/mediacms/classes/plugininfo.php
Executable file
@@ -0,0 +1,224 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
namespace tiny_mediacms;
|
||||||
|
|
||||||
|
use context;
|
||||||
|
use context_course;
|
||||||
|
use context_module;
|
||||||
|
use editor_tiny\editor;
|
||||||
|
use editor_tiny\plugin;
|
||||||
|
use editor_tiny\plugin_with_buttons;
|
||||||
|
use editor_tiny\plugin_with_configuration;
|
||||||
|
use editor_tiny\plugin_with_menuitems;
|
||||||
|
use moodle_url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny media plugin.
|
||||||
|
*
|
||||||
|
* @package tiny_media
|
||||||
|
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menuitems, plugin_with_configuration {
|
||||||
|
/**
|
||||||
|
* Whether the plugin is enabled
|
||||||
|
*
|
||||||
|
* @param context $context The context that the editor is used within
|
||||||
|
* @param array $options The options passed in when requesting the editor
|
||||||
|
* @param array $fpoptions The filepicker options passed in when requesting the editor
|
||||||
|
* @param editor $editor The editor instance in which the plugin is initialised
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function is_enabled(
|
||||||
|
context $context,
|
||||||
|
array $options,
|
||||||
|
array $fpoptions,
|
||||||
|
?editor $editor = null
|
||||||
|
): bool {
|
||||||
|
// Disabled if:
|
||||||
|
// - Not logged in or guest.
|
||||||
|
// - Files are not allowed.
|
||||||
|
// - Only URL are supported.
|
||||||
|
$canhavefiles = !empty($options['maxfiles']);
|
||||||
|
$canhaveexternalfiles = !empty($options['return_types']) && ($options['return_types'] & FILE_EXTERNAL);
|
||||||
|
|
||||||
|
return isloggedin() && !isguestuser() && ($canhavefiles || $canhaveexternalfiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_available_buttons(): array {
|
||||||
|
return [
|
||||||
|
'tiny_mediacms/tiny_mediacms_image',
|
||||||
|
'tiny_mediacms/tiny_mediacms_video',
|
||||||
|
'tiny_mediacms/tiny_mediacms_iframe',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_available_menuitems(): array {
|
||||||
|
return [
|
||||||
|
'tiny_mediacms/tiny_mediacms_image',
|
||||||
|
'tiny_mediacms/tiny_mediacms_video',
|
||||||
|
'tiny_mediacms/tiny_mediacms_iframe',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_plugin_configuration_for_context(
|
||||||
|
context $context,
|
||||||
|
array $options,
|
||||||
|
array $fpoptions,
|
||||||
|
?editor $editor = null
|
||||||
|
): array {
|
||||||
|
|
||||||
|
// TODO Fetch the actual permissions.
|
||||||
|
$permissions = [
|
||||||
|
'image' => [
|
||||||
|
'filepicker' => true,
|
||||||
|
],
|
||||||
|
'embed' => [
|
||||||
|
'filepicker' => true,
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get LTI configuration for MediaCMS iframe library.
|
||||||
|
$lticonfig = self::get_lti_configuration($context);
|
||||||
|
|
||||||
|
// Get auto-convert configuration.
|
||||||
|
$autoconvertconfig = self::get_autoconvert_configuration();
|
||||||
|
|
||||||
|
return array_merge([
|
||||||
|
'permissions' => $permissions,
|
||||||
|
], self::get_file_manager_configuration($context, $options, $fpoptions), $lticonfig, $autoconvertconfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the auto-convert configuration for pasted MediaCMS URLs.
|
||||||
|
*
|
||||||
|
* @return array Auto-convert configuration data
|
||||||
|
*/
|
||||||
|
protected static function get_autoconvert_configuration(): array {
|
||||||
|
$baseurl = get_config('tiny_mediacms', 'autoconvert_baseurl');
|
||||||
|
|
||||||
|
// Helper function to get config with default value of true.
|
||||||
|
$getboolconfig = function($name) {
|
||||||
|
$value = get_config('tiny_mediacms', $name);
|
||||||
|
// If the setting hasn't been saved yet (false/empty), default to true.
|
||||||
|
// Only return false if explicitly set to '0'.
|
||||||
|
return $value !== '0' && $value !== 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
|
'data' => [
|
||||||
|
'autoConvertEnabled' => $getboolconfig('autoconvertenabled'),
|
||||||
|
'autoConvertBaseUrl' => !empty($baseurl) ? $baseurl : '',
|
||||||
|
'autoConvertOptions' => [
|
||||||
|
'showTitle' => $getboolconfig('autoconvert_showtitle'),
|
||||||
|
'linkTitle' => $getboolconfig('autoconvert_linktitle'),
|
||||||
|
'showRelated' => $getboolconfig('autoconvert_showrelated'),
|
||||||
|
'showUserAvatar' => $getboolconfig('autoconvert_showuseravatar'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the LTI configuration for the MediaCMS iframe library.
|
||||||
|
*
|
||||||
|
* @param context $context The context that the editor is used within
|
||||||
|
* @return array LTI configuration data
|
||||||
|
*/
|
||||||
|
protected static function get_lti_configuration(context $context): array {
|
||||||
|
global $COURSE;
|
||||||
|
|
||||||
|
// Get the configured LTI tool ID from plugin settings.
|
||||||
|
$ltitoolid = get_config('tiny_mediacms', 'ltitoolid');
|
||||||
|
|
||||||
|
// Determine the course ID from context.
|
||||||
|
$courseid = 0;
|
||||||
|
if ($context instanceof context_course) {
|
||||||
|
$courseid = $context->instanceid;
|
||||||
|
} else if ($context instanceof context_module) {
|
||||||
|
// Get the course from the module context.
|
||||||
|
$coursecontext = $context->get_course_context(false);
|
||||||
|
if ($coursecontext) {
|
||||||
|
$courseid = $coursecontext->instanceid;
|
||||||
|
}
|
||||||
|
} else if (!empty($COURSE->id) && $COURSE->id != SITEID) {
|
||||||
|
// Fall back to the global $COURSE if available and not the site.
|
||||||
|
$courseid = $COURSE->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the content item URL for LTI Deep Linking.
|
||||||
|
// This URL initiates the LTI Deep Linking flow which allows users
|
||||||
|
// to select content (like videos) from the tool provider.
|
||||||
|
$contentitemurl = '';
|
||||||
|
if (!empty($ltitoolid) && $courseid > 0) {
|
||||||
|
$contentitemurl = (new moodle_url('/mod/lti/contentitem.php', [
|
||||||
|
'id' => $ltitoolid,
|
||||||
|
'course' => $courseid,
|
||||||
|
]))->out(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'lti' => [
|
||||||
|
'toolId' => !empty($ltitoolid) ? (int) $ltitoolid : 0,
|
||||||
|
'courseId' => $courseid,
|
||||||
|
'contentItemUrl' => $contentitemurl,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function get_file_manager_configuration(
|
||||||
|
context $context,
|
||||||
|
array $options,
|
||||||
|
array $fpoptions
|
||||||
|
): array {
|
||||||
|
global $USER;
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
'area' => [],
|
||||||
|
'usercontext' => \context_user::instance($USER->id)->id,
|
||||||
|
];
|
||||||
|
|
||||||
|
$keys = [
|
||||||
|
'itemid',
|
||||||
|
'areamaxbytes',
|
||||||
|
'maxbytes',
|
||||||
|
'subdirs',
|
||||||
|
'return_types',
|
||||||
|
'removeorphaneddrafts',
|
||||||
|
];
|
||||||
|
if (isset($options['context'])) {
|
||||||
|
if (is_object($options['context'])) {
|
||||||
|
$params['area']['context'] = $options['context']->id;
|
||||||
|
} else {
|
||||||
|
$params['area']['context'] = $options['context'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (isset($options[$key])) {
|
||||||
|
$params['area'][$key] = $options[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'storeinrepo' => true,
|
||||||
|
'data' => [
|
||||||
|
'params' => $params,
|
||||||
|
'fpoptions' => $fpoptions,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
30
lms-plugins/mediacms-moodle/tiny/mediacms/classes/privacy/provider.php
Executable file
30
lms-plugins/mediacms-moodle/tiny/mediacms/classes/privacy/provider.php
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
namespace tiny_mediacms\privacy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Privacy Subsystem implementation for the media plugin for TinyMCE.
|
||||||
|
*
|
||||||
|
* @package tiny_media
|
||||||
|
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class provider implements \core_privacy\local\metadata\null_provider {
|
||||||
|
public static function get_reason(): string {
|
||||||
|
return 'privacy:metadata';
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/deprecated.txt
Executable file
7
lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/deprecated.txt
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
alignment,tiny_media
|
||||||
|
alignment_bottom,tiny_media
|
||||||
|
alignment_left,tiny_media
|
||||||
|
alignment_middle,tiny_media
|
||||||
|
alignment_right,tiny_media
|
||||||
|
alignment_top,tiny_media
|
||||||
|
helplinktext,tiny_media
|
||||||
197
lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/tiny_mediacms.php
Executable file
197
lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/tiny_mediacms.php
Executable file
@@ -0,0 +1,197 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strings for component 'tiny_mediacms', language 'en'.
|
||||||
|
*
|
||||||
|
* @package tiny_mediacms
|
||||||
|
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
$string['addcaptionstrack'] = 'Add caption track';
|
||||||
|
$string['addchapterstrack'] = 'Add chapter track';
|
||||||
|
$string['adddescriptionstrack'] = 'Add description track';
|
||||||
|
$string['addfilesdrop'] = 'Drag and drop an image to upload, or click to select';
|
||||||
|
$string['addmetadatatrack'] = 'Add metadata track';
|
||||||
|
$string['addsource_help'] = 'You are recommended to provide an alternative media source, as desktop and mobile browsers support different file formats.';
|
||||||
|
$string['addsource'] = 'Add alternative source';
|
||||||
|
$string['addsubtitlestrack'] = 'Add subtitle track';
|
||||||
|
$string['addurl'] = 'Add';
|
||||||
|
$string['advancedsettings'] = 'Advanced settings';
|
||||||
|
$string['audio'] = 'Audio';
|
||||||
|
$string['audiosourcelabel'] = 'Audio source URL';
|
||||||
|
$string['autoplay'] = 'Play automatically';
|
||||||
|
$string['browserepositories'] = 'Browse repositories...';
|
||||||
|
$string['browserepositoriesimage'] = 'Browse repositories';
|
||||||
|
$string['captions_help'] = 'Captions may be used to describe everything happening in the track, including non-verbal sounds such as a phone ringing.';
|
||||||
|
$string['captions'] = 'Captions';
|
||||||
|
$string['captionssourcelabel'] = 'Caption track URL';
|
||||||
|
$string['chapters_help'] = 'Chapter titles may be provided for use in navigating the media resource.';
|
||||||
|
$string['chapters'] = 'Chapters';
|
||||||
|
$string['chapterssourcelabel'] = 'Chapter track URL';
|
||||||
|
$string['constrain'] = 'Keep proportion';
|
||||||
|
$string['controls'] = 'Show controls';
|
||||||
|
$string['createmedia'] = 'Insert favorite media';
|
||||||
|
$string['default'] = 'Default';
|
||||||
|
$string['deleteimage'] = 'Delete image';
|
||||||
|
$string['deleteimagewarning'] = 'Are you sure you want to remove the image?';
|
||||||
|
$string['deleteselected'] = 'Delete selected files';
|
||||||
|
$string['descriptions_help'] = 'Audio descriptions may be used to provide a narration which explains visual details not apparent from the audio alone.';
|
||||||
|
$string['descriptions'] = 'Descriptions';
|
||||||
|
$string['descriptionssourcelabel'] = 'Description track URL';
|
||||||
|
$string['displayoptions'] = 'Display options';
|
||||||
|
$string['enteralt'] = 'How would you describe this image to someone who can\'t see it?';
|
||||||
|
$string['entername'] = 'Name';
|
||||||
|
$string['entersource'] = 'Source URL';
|
||||||
|
$string['entertitle'] = 'Title';
|
||||||
|
$string['enterurl'] = 'Add via URL';
|
||||||
|
$string['enterurlor'] = 'Or add via URL';
|
||||||
|
$string['filemanager'] = 'Favorite file manager';
|
||||||
|
$string['hasmissingfiles'] = 'Warning! The following files that are referenced in the text area appear to be missing:';
|
||||||
|
$string['height'] = 'Height';
|
||||||
|
$string['imagebuttontitle'] = 'Favorite Image';
|
||||||
|
$string['imagedetails'] = 'Image details';
|
||||||
|
$string['imageproperties'] = 'Image properties';
|
||||||
|
$string['imageurlrequired'] = 'An image must have a valid URL.';
|
||||||
|
$string['insertimage'] = 'Insert favorite image';
|
||||||
|
$string['label'] = 'Label';
|
||||||
|
$string['languagesavailable'] = 'Languages available';
|
||||||
|
$string['languagesinstalled'] = 'Languages installed';
|
||||||
|
$string['link'] = 'Link';
|
||||||
|
$string['loading'] = 'Preparing the image';
|
||||||
|
$string['loop'] = 'Loop';
|
||||||
|
$string['managefiles'] = 'Manage favorite files';
|
||||||
|
$string['mediabuttontitle'] = 'Favorite Multimedia';
|
||||||
|
$string['mediamanagerbuttontitle'] = 'Favorite media manager';
|
||||||
|
$string['mediamanagerproperties'] = 'Favorite media manager';
|
||||||
|
$string['metadata_help'] = 'Metadata tracks, for use from a script, may be used only if the player supports metadata.';
|
||||||
|
$string['metadata'] = 'Metadata';
|
||||||
|
$string['metadatasourcelabel'] = 'Metadata track URL';
|
||||||
|
$string['missingfiles'] = 'Missing files';
|
||||||
|
$string['mute'] = 'Muted';
|
||||||
|
$string['pluginname'] = 'MediaCMS';
|
||||||
|
$string['presentation'] = 'This image is decorative only';
|
||||||
|
$string['presentationoraltrequired'] = 'An image must have a description, unless it is marked as decorative only.';
|
||||||
|
$string['privacy:metadata'] = 'The favorite media plugin for TinyMCE does not store any personal data.';
|
||||||
|
$string['remove'] = 'Remove';
|
||||||
|
$string['repositorynotpermitted'] = 'Paste an image link in the field below.';
|
||||||
|
$string['repositoryuploadnotpermitted'] = 'Paste an image link in the field below or<br>click the Browse Repositories button.';
|
||||||
|
$string['saveimage'] = 'Save';
|
||||||
|
$string['size'] = 'Width x height (in pixels)';
|
||||||
|
$string['sizecustom'] = 'Custom size';
|
||||||
|
$string['sizecustom_help'] = 'This image is just a preview. Changes to its size will be visible after you save it.';
|
||||||
|
$string['sizeoriginal'] = 'Original size';
|
||||||
|
$string['srclang'] = 'Language';
|
||||||
|
$string['subtitles_help'] = 'Subtitles may be used to provide a transcription or translation of the dialogue.';
|
||||||
|
$string['subtitles'] = 'Subtitles';
|
||||||
|
$string['subtitlessourcelabel'] = 'Subtitle track URL';
|
||||||
|
$string['tracks_help'] = 'Subtitles, captions, chapters and descriptions can be added via a WebVTT (Web Video Text Tracks) format file. Track labels will be shown in the selection drop-down menu. For each type of track, any track set as default will be pre-selected at the start of the video.';
|
||||||
|
$string['tracks'] = 'Subtitles and captions';
|
||||||
|
$string['unusedfilesdesc'] = 'The following embedded files are not used in the text area:';
|
||||||
|
$string['unusedfilesheader'] = 'Unused files';
|
||||||
|
$string['unusedfilesremovalnotice'] = 'Any unused files will be automatically deleted when saving changes.';
|
||||||
|
$string['updatemedia'] = 'Update favorite media';
|
||||||
|
$string['uploading'] = 'Uploading';
|
||||||
|
$string['video'] = 'Video';
|
||||||
|
$string['videoheight'] = 'Video height';
|
||||||
|
$string['videosourcelabel'] = 'Video source URL';
|
||||||
|
$string['videowidth'] = 'Video width';
|
||||||
|
$string['width'] = 'Width';
|
||||||
|
|
||||||
|
// Iframe embed strings.
|
||||||
|
$string['iframebuttontitle'] = 'Insert MediaCMS Video';
|
||||||
|
$string['iframemodaltitle'] = 'Insert MediaCMS Video';
|
||||||
|
$string['iframeurl'] = 'MediaCMS Video URL or embed code';
|
||||||
|
$string['iframeurlplaceholder'] = 'Paste MediaCMS Video URL or iframe embed code';
|
||||||
|
$string['iframeurlinvalid'] = 'Please enter a valid MediaCMS Video URL or embed code';
|
||||||
|
$string['embedoptions'] = 'Embed Options';
|
||||||
|
$string['showtitle'] = 'Show title';
|
||||||
|
$string['linktitle'] = 'Link title';
|
||||||
|
$string['showrelated'] = 'Show related';
|
||||||
|
$string['showuseravatar'] = 'Show user avatar';
|
||||||
|
$string['responsive'] = 'Responsive';
|
||||||
|
$string['startat'] = 'Start at';
|
||||||
|
$string['aspectratio'] = 'Aspect Ratio';
|
||||||
|
$string['aspectratio_16_9'] = '16:9';
|
||||||
|
$string['aspectratio_4_3'] = '4:3';
|
||||||
|
$string['aspectratio_1_1'] = '1:1';
|
||||||
|
$string['aspectratio_custom'] = 'Custom';
|
||||||
|
$string['dimensions'] = 'Dimensions';
|
||||||
|
$string['preview'] = 'Preview';
|
||||||
|
$string['insertiframe'] = 'Insert video';
|
||||||
|
$string['updateiframe'] = 'Update video';
|
||||||
|
$string['removeiframe'] = 'Remove video';
|
||||||
|
$string['removeiframeconfirm'] = 'Are you sure you want to remove this video from the editor?';
|
||||||
|
|
||||||
|
// Iframe modal tabs.
|
||||||
|
$string['tabembedurl'] = 'Embed URL';
|
||||||
|
$string['tabvideolibrary'] = 'Video Library';
|
||||||
|
$string['tabvideolibraryiframe'] = 'Video Library from Iframe';
|
||||||
|
|
||||||
|
// Video library strings.
|
||||||
|
$string['librarysearchplaceholder'] = 'Search videos...';
|
||||||
|
$string['librarysortnewest'] = 'Newest first';
|
||||||
|
$string['librarysortoldest'] = 'Oldest first';
|
||||||
|
$string['librarysorttitle'] = 'Title A-Z';
|
||||||
|
$string['librarysortviews'] = 'Most views';
|
||||||
|
$string['libraryloading'] = 'Loading videos...';
|
||||||
|
$string['libraryempty'] = 'No videos found';
|
||||||
|
$string['libraryerror'] = 'Failed to load videos';
|
||||||
|
$string['libraryretry'] = 'Retry';
|
||||||
|
$string['libraryprev'] = 'Previous';
|
||||||
|
$string['librarynext'] = 'Next';
|
||||||
|
$string['libraryselect'] = 'Select';
|
||||||
|
$string['librarypage'] = 'Page {$a->current} of {$a->total}';
|
||||||
|
$string['libraryvideoselected'] = 'Video selected. Configure embed options below.';
|
||||||
|
|
||||||
|
// LTI settings strings.
|
||||||
|
$string['ltitoolid'] = 'MediaCMS LTI tool ID';
|
||||||
|
$string['ltitoolid_desc'] = 'The ID of the external tool (LTI) configured in Moodle for MediaCMS. This enables the authenticated video library in the editor.';
|
||||||
|
$string['ltitoolid_help'] = 'To find the LTI tool ID, go to Site administration > Plugins > Activity modules > External tool > Manage tools. The ID is shown in the URL when editing a tool (e.g., id=2).';
|
||||||
|
|
||||||
|
// Iframe library from LTI strings.
|
||||||
|
$string['iframelibraryloading'] = 'Loading MediaCMS video library...';
|
||||||
|
$string['iframelibraryplaceholder'] = 'Click here to load the video library';
|
||||||
|
$string['iframelibraryerror'] = 'Failed to load the video library. Please check your LTI configuration.';
|
||||||
|
$string['iframelibrarynotconfigured'] = 'The MediaCMS LTI tool has not been configured. Please contact your administrator.';
|
||||||
|
|
||||||
|
// Auto-convert settings strings.
|
||||||
|
$string['autoconvertheading'] = 'Auto-convert MediaCMS URLs';
|
||||||
|
$string['autoconvertheading_desc'] = 'Configure automatic conversion of pasted MediaCMS URLs to embedded videos.';
|
||||||
|
$string['autoconvertenabled'] = 'Enable auto-convert';
|
||||||
|
$string['autoconvertenabled_desc'] = 'When enabled, pasting a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=VIDEO_ID) into the editor will automatically convert it to an embedded video player.';
|
||||||
|
$string['autoconvert_baseurl'] = 'MediaCMS base URL';
|
||||||
|
$string['autoconvert_baseurl_desc'] = 'The base URL of your MediaCMS instance (e.g., https://deic.mediacms.io). If specified, only URLs from this domain will be auto-converted. Leave empty to allow any MediaCMS URL.';
|
||||||
|
$string['autoconvert_showtitle'] = 'Show video title';
|
||||||
|
$string['autoconvert_showtitle_desc'] = 'Display the video title in the embedded player.';
|
||||||
|
$string['autoconvert_linktitle'] = 'Link video title';
|
||||||
|
$string['autoconvert_linktitle_desc'] = 'Make the video title clickable, linking to the original video page.';
|
||||||
|
$string['autoconvert_showrelated'] = 'Show related videos';
|
||||||
|
$string['autoconvert_showrelated_desc'] = 'Display related videos after the current video ends.';
|
||||||
|
$string['autoconvert_showuseravatar'] = 'Show user avatar';
|
||||||
|
$string['autoconvert_showuseravatar_desc'] = 'Display the uploader\'s avatar in the embedded player.';
|
||||||
|
|
||||||
|
// Deprecated since Moodle 4.4.
|
||||||
|
$string['alignment'] = 'Alignment';
|
||||||
|
$string['alignment_bottom'] = 'Bottom';
|
||||||
|
$string['alignment_left'] = 'Left';
|
||||||
|
$string['alignment_middle'] = 'Middle';
|
||||||
|
$string['alignment_right'] = 'Right';
|
||||||
|
$string['alignment_top'] = 'Top';
|
||||||
|
|
||||||
|
// Deprecated since Moodle 4.5.
|
||||||
|
$string['helplinktext'] = 'Favorite media helper';
|
||||||
148
lms-plugins/mediacms-moodle/tiny/mediacms/manage.php
Executable file
148
lms-plugins/mediacms-moodle/tiny/mediacms/manage.php
Executable file
@@ -0,0 +1,148 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage files in user draft area attached to texteditor.
|
||||||
|
*
|
||||||
|
* @package tiny_mediacms
|
||||||
|
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
require(__DIR__ . '/../../../../../config.php');
|
||||||
|
require_once($CFG->libdir . '/filestorage/file_storage.php');
|
||||||
|
require_once($CFG->dirroot . '/repository/lib.php');
|
||||||
|
|
||||||
|
$itemid = required_param('itemid', PARAM_INT) ?? 0;
|
||||||
|
$maxbytes = optional_param('maxbytes', 0, PARAM_INT);
|
||||||
|
$subdirs = optional_param('subdirs', 0, PARAM_INT);
|
||||||
|
$acceptedtypes = optional_param('accepted_types', '*', PARAM_RAW); // TODO Not yet passed to this script.
|
||||||
|
$returntypes = optional_param('return_types', null, PARAM_INT);
|
||||||
|
$areamaxbytes = optional_param('areamaxbytes', FILE_AREA_MAX_BYTES_UNLIMITED, PARAM_INT);
|
||||||
|
$contextid = optional_param('context', SYSCONTEXTID, PARAM_INT);
|
||||||
|
$elementid = optional_param('elementid', '', PARAM_TEXT);
|
||||||
|
$removeorphaneddrafts = optional_param('removeorphaneddrafts', 0, PARAM_INT);
|
||||||
|
|
||||||
|
$context = context::instance_by_id($contextid);
|
||||||
|
if ($context->contextlevel == CONTEXT_MODULE) {
|
||||||
|
// Module context.
|
||||||
|
$cm = $DB->get_record('course_modules', ['id' => $context->instanceid]);
|
||||||
|
require_login($cm->course, true, $cm);
|
||||||
|
} else if (($coursecontext = $context->get_course_context(false)) && $coursecontext->id != SITEID) {
|
||||||
|
// Course context or block inside the course.
|
||||||
|
require_login($coursecontext->instanceid);
|
||||||
|
$PAGE->set_context($context);
|
||||||
|
} else {
|
||||||
|
// Block that is not inside the course, user or system context.
|
||||||
|
require_login();
|
||||||
|
$PAGE->set_context($context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guests can never manage files.
|
||||||
|
if (isguestuser()) {
|
||||||
|
throw new \moodle_exception('noguest');
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = get_string('managefiles', 'tiny_mediacms');
|
||||||
|
|
||||||
|
$url = new moodle_url('/lib/editor/tiny/plugins/mediacms/manage.php', [
|
||||||
|
'itemid' => $itemid,
|
||||||
|
'maxbytes' => $maxbytes,
|
||||||
|
'subdirs' => $subdirs,
|
||||||
|
'accepted_types' => $acceptedtypes,
|
||||||
|
'return_types' => $returntypes,
|
||||||
|
'areamaxbytes' => $areamaxbytes,
|
||||||
|
'context' => $contextid,
|
||||||
|
'elementid' => $elementid,
|
||||||
|
'removeorphaneddrafts' => $removeorphaneddrafts,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$PAGE->set_url($url);
|
||||||
|
$PAGE->set_title($title);
|
||||||
|
$PAGE->set_heading($title);
|
||||||
|
$PAGE->set_pagelayout('popup');
|
||||||
|
|
||||||
|
if ($returntypes !== null) {
|
||||||
|
// Links are allowed in textarea but never allowed in filemanager.
|
||||||
|
$returntypes = $returntypes & ~FILE_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are the options required for the filepicker.
|
||||||
|
$options = [
|
||||||
|
'subdirs' => $subdirs,
|
||||||
|
'maxbytes' => $maxbytes,
|
||||||
|
'maxfiles' => -1,
|
||||||
|
'accepted_types' => $acceptedtypes,
|
||||||
|
'areamaxbytes' => $areamaxbytes,
|
||||||
|
'return_types' => $returntypes,
|
||||||
|
'context' => $context
|
||||||
|
];
|
||||||
|
|
||||||
|
$usercontext = context_user::instance($USER->id);
|
||||||
|
$fs = get_file_storage();
|
||||||
|
$files = $fs->get_directory_files($usercontext->id, 'user', 'draft', $itemid, '/', !empty($subdirs), false);
|
||||||
|
$filenames = [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$filenames[$file->get_pathnamehash()] = ltrim($file->get_filepath(), '/') . $file->get_filename();
|
||||||
|
}
|
||||||
|
|
||||||
|
$mform = new tiny_mediacms\form\manage_files_form(null, [
|
||||||
|
'context' => $usercontext,
|
||||||
|
'options' => $options,
|
||||||
|
'draftitemid' => $itemid,
|
||||||
|
'files' => $filenames,
|
||||||
|
'elementid' => $elementid,
|
||||||
|
'removeorphaneddrafts' => $removeorphaneddrafts,
|
||||||
|
], 'post', '', [
|
||||||
|
'id' => 'tiny_mediacms_form',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($data = $mform->get_data()) {
|
||||||
|
if (!empty($data->deletefile)) {
|
||||||
|
foreach (array_keys($data->deletefile) as $filehash) {
|
||||||
|
if ($file = $fs->get_file_by_hash($filehash)) {
|
||||||
|
// Make sure the user didn't modify the filehash to delete another file.
|
||||||
|
if ($file->get_component() !== 'user' || $file->get_filearea() !== 'draft') {
|
||||||
|
// The file must belong to the user/draft area.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($file->get_contextid() != $usercontext->id) {
|
||||||
|
// The user must own the file - that is it must be in their user draft file area.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($file->get_itemid() != $itemid) {
|
||||||
|
// It must be the file they requested be deleted.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$file->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Redirect to prevent re-posting the form.
|
||||||
|
redirect($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mform->set_data(array_merge($options, [
|
||||||
|
'files_filemanager' => $itemid,
|
||||||
|
'itemid' => $itemid,
|
||||||
|
'elementid' => $elementid,
|
||||||
|
'context' => $context->id,
|
||||||
|
]));
|
||||||
|
|
||||||
|
echo $OUTPUT->header();
|
||||||
|
$mform->display();
|
||||||
|
echo $OUTPUT->footer();
|
||||||
4
lms-plugins/mediacms-moodle/tiny/mediacms/pix/filemanager.svg
Executable file
4
lms-plugins/mediacms-moodle/tiny/mediacms/pix/filemanager.svg
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 238 B |
4
lms-plugins/mediacms-moodle/tiny/mediacms/pix/icon.svg
Executable file
4
lms-plugins/mediacms-moodle/tiny/mediacms/pix/icon.svg
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<circle cx="50" cy="50" r="48" fill="#2EAF5A"/>
|
||||||
|
<polygon points="38,28 38,72 75,50" fill="#FFFFFF"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 200 B |
97
lms-plugins/mediacms-moodle/tiny/mediacms/settings.php
Executable file
97
lms-plugins/mediacms-moodle/tiny/mediacms/settings.php
Executable file
@@ -0,0 +1,97 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings for the tiny_mediacms plugin.
|
||||||
|
*
|
||||||
|
* @package tiny_mediacms
|
||||||
|
* @copyright 2024
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
if ($ADMIN->fulltree) {
|
||||||
|
// LTI Tool ID setting.
|
||||||
|
// This should be the ID of the external tool (LTI) configured in Moodle for MediaCMS.
|
||||||
|
$setting = new admin_setting_configtext(
|
||||||
|
'tiny_mediacms/ltitoolid',
|
||||||
|
new lang_string('ltitoolid', 'tiny_mediacms'),
|
||||||
|
new lang_string('ltitoolid_desc', 'tiny_mediacms'),
|
||||||
|
'',
|
||||||
|
PARAM_INT
|
||||||
|
);
|
||||||
|
$settings->add($setting);
|
||||||
|
|
||||||
|
// Auto-convert heading.
|
||||||
|
$settings->add(new admin_setting_heading(
|
||||||
|
'tiny_mediacms/autoconvertheading',
|
||||||
|
new lang_string('autoconvertheading', 'tiny_mediacms'),
|
||||||
|
new lang_string('autoconvertheading_desc', 'tiny_mediacms')
|
||||||
|
));
|
||||||
|
|
||||||
|
// Enable/disable auto-convert of pasted MediaCMS URLs.
|
||||||
|
$setting = new admin_setting_configcheckbox(
|
||||||
|
'tiny_mediacms/autoconvertenabled',
|
||||||
|
new lang_string('autoconvertenabled', 'tiny_mediacms'),
|
||||||
|
new lang_string('autoconvertenabled_desc', 'tiny_mediacms'),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
$settings->add($setting);
|
||||||
|
|
||||||
|
// MediaCMS base URL for auto-convert.
|
||||||
|
$setting = new admin_setting_configtext(
|
||||||
|
'tiny_mediacms/autoconvert_baseurl',
|
||||||
|
new lang_string('autoconvert_baseurl', 'tiny_mediacms'),
|
||||||
|
new lang_string('autoconvert_baseurl_desc', 'tiny_mediacms'),
|
||||||
|
'',
|
||||||
|
PARAM_URL
|
||||||
|
);
|
||||||
|
$settings->add($setting);
|
||||||
|
|
||||||
|
// Auto-convert embed options.
|
||||||
|
$setting = new admin_setting_configcheckbox(
|
||||||
|
'tiny_mediacms/autoconvert_showtitle',
|
||||||
|
new lang_string('autoconvert_showtitle', 'tiny_mediacms'),
|
||||||
|
new lang_string('autoconvert_showtitle_desc', 'tiny_mediacms'),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
$settings->add($setting);
|
||||||
|
|
||||||
|
$setting = new admin_setting_configcheckbox(
|
||||||
|
'tiny_mediacms/autoconvert_linktitle',
|
||||||
|
new lang_string('autoconvert_linktitle', 'tiny_mediacms'),
|
||||||
|
new lang_string('autoconvert_linktitle_desc', 'tiny_mediacms'),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
$settings->add($setting);
|
||||||
|
|
||||||
|
$setting = new admin_setting_configcheckbox(
|
||||||
|
'tiny_mediacms/autoconvert_showrelated',
|
||||||
|
new lang_string('autoconvert_showrelated', 'tiny_mediacms'),
|
||||||
|
new lang_string('autoconvert_showrelated_desc', 'tiny_mediacms'),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
$settings->add($setting);
|
||||||
|
|
||||||
|
$setting = new admin_setting_configcheckbox(
|
||||||
|
'tiny_mediacms/autoconvert_showuseravatar',
|
||||||
|
new lang_string('autoconvert_showuseravatar', 'tiny_mediacms'),
|
||||||
|
new lang_string('autoconvert_showuseravatar_desc', 'tiny_mediacms'),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
$settings->add($setting);
|
||||||
|
}
|
||||||
79
lms-plugins/mediacms-moodle/tiny/mediacms/styles.css
Executable file
79
lms-plugins/mediacms-moodle/tiny/mediacms/styles.css
Executable file
@@ -0,0 +1,79 @@
|
|||||||
|
#tiny_mediacms_form {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tiny_mediacms_form #id_deletefileshdr {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tiny_mediacms_form.has-unused-files #id_deletefileshdr {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tiny_mediacms_form #id_missingfileshdr {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tiny_mediacms_form.has-missing-files #id_missingfileshdr {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe.mmcms_iframe {
|
||||||
|
height: 650px;
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.missing-files ol {
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.missing-files ol li {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiny_imagecms_form .tiny_imagecms_dropzone_container {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiny_imagecms_form .tiny_imagecms_dropzone_container .dropzone-label {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiny_imagecms_form .tiny_imagecms_loader_container {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiny_imagecms_form .tiny_imagecms_preview_box {
|
||||||
|
height: 300px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiny_imagecms_form .tiny_imagecms_deleteicon {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
background: rgba(255, 255, 255, 1);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 4px 5px 5px 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiny_imagecms_form .tiny_imagecms_deleteicon .fa-trash {
|
||||||
|
color: #1d2125;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.tiny_imagecms_form .tiny_imagecms_properties_col {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/embed_media_audio
|
||||||
|
|
||||||
|
Embed media audio template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<audio{{!
|
||||||
|
}}{{#showControls}} controls="true" {{/showControls}}{{!
|
||||||
|
}}{{#loop}} loop="true"{{/loop}}{{!
|
||||||
|
}}{{#muted}} muted="true"{{/muted}}{{!
|
||||||
|
}}{{#autoplay}} autoplay="true"{{/autoplay}}{{!
|
||||||
|
}}{{#title}} title="{{.}}"{{/title}}{{!
|
||||||
|
}}>
|
||||||
|
{{#sources}}<source src="{{.}}"/>{{/sources}}
|
||||||
|
{{#tracks}}
|
||||||
|
<track src="{{track}}" kind="{{kind}}" srclang="{{srclang}}" label="{{label}}"{{!
|
||||||
|
}}{{#defaultTrack}} default="true"{{/defaultTrack}}{{!
|
||||||
|
}}>
|
||||||
|
{{/tracks}}
|
||||||
|
{{#description}}{{.}}{{/description}}
|
||||||
|
</audio>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/embed_media_link
|
||||||
|
|
||||||
|
Embed media link template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<a href="{{url}}"{{!
|
||||||
|
}}{{#width}} data-width="{{.}}"{{/width}}{{!
|
||||||
|
}}{{#height}} data-height="{{.}}"{{/height}}{{!
|
||||||
|
}}>{{!
|
||||||
|
}}{{#name}}{{.}}{{/name}}{{!
|
||||||
|
}}{{^name}}{{url}}{{/name}}{{!
|
||||||
|
}}</a>
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/embed_media_modal
|
||||||
|
|
||||||
|
Embed media modal template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{{< core/modal }}
|
||||||
|
|
||||||
|
{{$title}}
|
||||||
|
{{#str}} modaltitle, tiny_h5p {{/str}}
|
||||||
|
{{/title}}
|
||||||
|
|
||||||
|
{{$body}}
|
||||||
|
<form class="tiny_mediacms_form" id="{{elementid}}_tiny_mediacms_form">
|
||||||
|
<ul class="root nav nav-tabs mb-1" role="tablist">
|
||||||
|
<li data-medium-type="link" class="nav-item">
|
||||||
|
<a class="nav-link {{# link }}active{{/ link }}" href="#{{elementid}}_link" role="tab" data-toggle="tab">
|
||||||
|
{{#str}} link, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-medium-type="video" class="nav-item">
|
||||||
|
<a class="nav-link {{# video }}active{{/ video }}" href="#{{elementid}}_video" role="tab" data-toggle="tab">
|
||||||
|
{{#str}} video, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-medium-type="audio" class="nav-item">
|
||||||
|
<a class="nav-link {{# audio }}active{{/ audio }}" href="#{{elementid}}_audio" role="tab" data-toggle="tab">
|
||||||
|
{{#str}} audio, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="root tab-content">
|
||||||
|
<div data-medium-type="link" class="tab-pane {{# link }}active{{/ link }}" id="{{elementid}}_link">
|
||||||
|
{{> tiny_mediacms/embed_media_modal_link }}
|
||||||
|
</div>
|
||||||
|
<div data-medium-type="video" class="tab-pane {{# video }}active{{/ video }}" id="{{elementid}}_video">
|
||||||
|
{{> tiny_mediacms/embed_media_modal_video}}
|
||||||
|
</div>
|
||||||
|
<div data-medium-type="audio" class="tab-pane {{# audio }}active{{/ audio }}" id="{{elementid}}_audio">
|
||||||
|
{{> tiny_mediacms/embed_media_modal_audio}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{/body}}
|
||||||
|
|
||||||
|
{{$footer}}
|
||||||
|
<button type="button" class="btn btn-primary" data-action="save">
|
||||||
|
{{#isupdating}}
|
||||||
|
{{#str}} updatemedia, tiny_mediacms {{/str}}
|
||||||
|
{{/isupdating}}
|
||||||
|
{{^isupdating}}
|
||||||
|
{{#str}} createmedia, tiny_mediacms {{/str}}
|
||||||
|
{{/isupdating}}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel, moodle {{/str}}</button>
|
||||||
|
{{/footer}}
|
||||||
|
|
||||||
|
{{/ core/modal }}
|
||||||
@@ -0,0 +1,802 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/embed_media_modal_audio
|
||||||
|
|
||||||
|
Embed media audio modal template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{{#audio.sources}}
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_media_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="audio-audio-url-input">
|
||||||
|
{{#str}} audiosourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="audio-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{.}}"/>
|
||||||
|
{{#showfilepicker}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepicker}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addsource, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
{{#addsourcehelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/addsourcehelpicon}}
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.sources}}
|
||||||
|
{{^audio}}
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_media_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="audio-audio-url-input">
|
||||||
|
{{#str}} audiosourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="audio-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepicker}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepicker}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addsource, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
{{#addsourcehelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/addsourcehelpicon}}
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio}}
|
||||||
|
<fieldset class="collapsible collapsed" id="{{elementid}}_audio-display-options">
|
||||||
|
<input name="mform_isexpanded_{{elementid}}_audio-display-options" type="hidden">
|
||||||
|
<legend class="d-flex align-items-center px-1">
|
||||||
|
<div class="position-relative d-flex ftoggler align-items-center position-relative me-1">
|
||||||
|
<a role="button" data-toggle="collapse" href="#adisplayoptions" aria-expanded="false"
|
||||||
|
aria-controls="adisplayoptions"
|
||||||
|
class="btn btn-icon me-3 icons-collapse-expand stretched-link fheader collapsed">
|
||||||
|
<span class="expanded-icon icon-no-margin p-2" title="{{#str}} collapse, moodle {{/str}}">
|
||||||
|
<i class="icon fa fa-chevron-down fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="collapsed-icon icon-no-margin p-2" title="{{#str}} expand, moodle {{/str}}">
|
||||||
|
<span class="dir-rtl-hide">
|
||||||
|
<i class="icon fa fa-chevron-right fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="dir-ltr-hide">
|
||||||
|
<i class="icon fa fa-chevron-left fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">{{#str}} displayoptions, tiny_mediacms {{/str}}</span>
|
||||||
|
</a>
|
||||||
|
<h3 class="d-flex align-self-stretch align-items-center mb-0" aria-hidden="true">
|
||||||
|
{{#str}} displayoptions, tiny_mediacms {{/str}}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</legend>
|
||||||
|
<div id="adisplayoptions" class="fcontainer collapseable collapse px-1">
|
||||||
|
<div class="tiny_mediacms_display_options">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="adisplayoptions_media-title-entry">{{#str}} entertitle, tiny_mediacms {{/str}}</label>
|
||||||
|
<input class="form-control fullwidth tiny_mediacms_title_entry" type="text" id="adisplayoptions_media-title-entry"
|
||||||
|
size="32" value="{{audio.title}}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="collapsible collapsed" id="{{elementid}}_audio-advanced-settings">
|
||||||
|
<input name="mform_isexpanded_{{elementid}}_audio-advanced-settings" type="hidden">
|
||||||
|
<legend class="d-flex align-items-center px-1">
|
||||||
|
<div class="position-relative d-flex ftoggler align-items-center position-relative me-1">
|
||||||
|
<a role="button" data-toggle="collapse" href="#aadvancedsettings" aria-expanded="false"
|
||||||
|
aria-controls="aadvancedsettings"
|
||||||
|
class="btn btn-icon me-3 icons-collapse-expand stretched-link fheader collapsed">
|
||||||
|
<span class="expanded-icon icon-no-margin p-2" title="{{#str}} collapse, moodle {{/str}}">
|
||||||
|
<i class="icon fa fa-chevron-down fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="collapsed-icon icon-no-margin p-2" title="{{#str}} expand, moodle {{/str}}">
|
||||||
|
<span class="dir-rtl-hide">
|
||||||
|
<i class="icon fa fa-chevron-right fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="dir-ltr-hide">
|
||||||
|
<i class="icon fa fa-chevron-left fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">{{#str}} advancedsettings, tiny_mediacms {{/str}}</span>
|
||||||
|
</a>
|
||||||
|
<h3 class="d-flex align-self-stretch align-items-center mb-0" aria-hidden="true">
|
||||||
|
{{#str}} advancedsettings, tiny_mediacms {{/str}}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</legend>
|
||||||
|
<div id="aadvancedsettings" class="fcontainer collapseable collapse px-1">
|
||||||
|
<div class="tiny_mediacms_advancedsettings">
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" checked="true" class="form-check-input tiny_mediacms_controls"
|
||||||
|
id="aadvancedsettings_media-controls-toggle" {{# audio.controls }}checked{{/ audio.controls }}/>
|
||||||
|
<label class="form-check-label" for="aadvancedsettings_media-controls-toggle">
|
||||||
|
{{#str}} controls, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_autoplay"
|
||||||
|
id="aadvancedsettings_media-autoplay-toggle" {{# audio.autoplay }}checked{{/ audio.autoplay }}/>
|
||||||
|
<label class="form-check-label" for="aadvancedsettings_media-autoplay-toggle">
|
||||||
|
{{#str}} autoplay, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_mute"
|
||||||
|
id="aadvancedsettings_media-mute-toggle" {{# audio.muted }}checked{{/ audio.muted }}/>
|
||||||
|
<label class="form-check-label" for="aadvancedsettings_media-mute-toggle">
|
||||||
|
{{#str}} mute, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_loop"
|
||||||
|
id="aadvancedsettings_media-loop-toggle" {{# audio.loop }}checked{{/ audio.loop }}/>
|
||||||
|
<label class="form-check-label" for="aadvancedsettings_media-loop-toggle">
|
||||||
|
{{#str}} loop, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="collapsible collapsed" id="{{elementid}}_audio-tracks">
|
||||||
|
<input name="mform_isexpanded_{{elementid}}_audio-tracks" type="hidden">
|
||||||
|
<legend class="d-flex align-items-center px-1">
|
||||||
|
<div class="position-relative d-flex ftoggler align-items-center position-relative me-1">
|
||||||
|
<a role="button" data-toggle="collapse" href="#atracks" aria-expanded="false"
|
||||||
|
aria-controls="atracks"
|
||||||
|
class="btn btn-icon me-3 icons-collapse-expand stretched-link fheader collapsed">
|
||||||
|
<span class="expanded-icon icon-no-margin p-2" title="{{#str}} collapse, moodle {{/str}}">
|
||||||
|
<i class="icon fa fa-chevron-down fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="collapsed-icon icon-no-margin p-2" title="{{#str}} expand, moodle {{/str}}">
|
||||||
|
<span class="dir-rtl-hide">
|
||||||
|
<i class="icon fa fa-chevron-right fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="dir-ltr-hide">
|
||||||
|
<i class="icon fa fa-chevron-left fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">{{#str}} tracks, tiny_mediacms {{/str}}</span>
|
||||||
|
</a>
|
||||||
|
<h3 class="d-flex align-self-stretch align-items-center mb-0" aria-hidden="true">
|
||||||
|
{{#str}} tracks, tiny_mediacms {{/str}}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
{{#trackshelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/trackshelpicon}}
|
||||||
|
</legend>
|
||||||
|
<div id="atracks" class="fcontainer collapseable collapse px-1">
|
||||||
|
<ul class="nav nav-tabs mb-3">
|
||||||
|
<li data-track-kind="subtitles" class="nav-item">
|
||||||
|
<a class="nav-link active" href="#{{elementid}}_atracks_subtitles"
|
||||||
|
role="tab" data-toggle="tab">
|
||||||
|
{{#str}} subtitles, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-track-kind="captions" class="nav-item">
|
||||||
|
<a class="nav-link" href="#{{elementid}}_atracks_captions" role="tab" data-toggle="tab">
|
||||||
|
{{#str}} captions, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-track-kind="descriptions" class="nav-item">
|
||||||
|
<a class="nav-link" href="#{{elementid}}_atracks_descriptions"
|
||||||
|
role="tab" data-toggle="tab">
|
||||||
|
{{#str}} descriptions, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-track-kind="chapters" class="nav-item">
|
||||||
|
<a class="nav-link" href="#{{elementid}}_atracks_chapters" role="tab" data-toggle="tab">
|
||||||
|
{{#str}} chapters, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-track-kind="metadata" class="nav-item">
|
||||||
|
<a class="nav-link" href="#{{elementid}}_atracks_metadata" role="tab" data-toggle="tab">
|
||||||
|
{{#str}} metadata, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div data-track-kind="subtitles" class="tab-pane active"
|
||||||
|
id="{{elementid}}_atracks_subtitles">
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#subtitleshelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/subtitleshelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#audio.tracks.subtitles}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="subtitle-audio-url-input">
|
||||||
|
{{#str}} subtitlessourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="subtitle-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="subtitle-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="subtitle-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="subtitle-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="subtitle-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addsubtitlestrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.subtitles}}
|
||||||
|
{{^audio.tracks.subtitles}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="subtitle-audio-url-input">
|
||||||
|
{{#str}} subtitlessourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="subtitle-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="subtitle-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="subtitle-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="subtitle-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="subtitle-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addsubtitlestrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.subtitles}}
|
||||||
|
</div>
|
||||||
|
<div data-track-kind="captions" class="tab-pane"
|
||||||
|
id="{{elementid}}_atracks_captions">
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#captionshelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/captionshelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#audio.tracks.captions}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="caption-audio-url-input">
|
||||||
|
{{#str}} captionssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="caption-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="caption-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="caption-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="caption-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="caption-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addcaptionstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.captions}}
|
||||||
|
{{^audio.tracks.captions}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="caption-audio-url-input">
|
||||||
|
{{#str}} captionssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="caption-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="caption-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="caption-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="caption-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="caption-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addcaptionstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.captions}}
|
||||||
|
</div>
|
||||||
|
<div data-track-kind="descriptions" class="tab-pane"
|
||||||
|
id="{{elementid}}_atracks_descriptions">
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#descriptionshelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/descriptionshelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#audio.tracks.descriptions}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="description-audio-url-input">
|
||||||
|
{{#str}} descriptionssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="description-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="description-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="description-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="description-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="description-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} adddescriptionstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.descriptions}}
|
||||||
|
{{^audio.tracks.descriptions}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="description-audio-url-input">
|
||||||
|
{{#str}} descriptionssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="description-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="description-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="description-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="description-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="description-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} adddescriptionstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.descriptions}}
|
||||||
|
</div>
|
||||||
|
<div data-track-kind="chapters" class="tab-pane"
|
||||||
|
id="{{elementid}}_atracks_chapters">
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#chaptershelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/chaptershelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#audio.tracks.chapters}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="chapter-audio-url-input">
|
||||||
|
{{#str}} chapterssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="chapter-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="chapter-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="chapter-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="chapter-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="chapter-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addchapterstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.chapters}}
|
||||||
|
{{^audio.tracks.chapters}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="chapter-audio-url-input">
|
||||||
|
{{#str}} chapterssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="chapter-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="chapter-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="chapter-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="chapter-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="chapter-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addchapterstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.chapters}}
|
||||||
|
</div>
|
||||||
|
<div data-track-kind="metadata" class="tab-pane"
|
||||||
|
id="{{elementid}}_atracks_metadata">
|
||||||
|
<div class="trackhelp">{{{helpStrings.metadata}}}</div>
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#metadatahelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/metadatahelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#audio.tracks.metadata}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="metadata-audio-url-input">
|
||||||
|
{{#str}} metadatasourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="metadata-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="metadata-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="metadata-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="metadata-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="metadata-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addmetadatatrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.metadata}}
|
||||||
|
{{^audio.tracks.metadata}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="metadata-audio-url-input">
|
||||||
|
{{#str}} metadatasourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="metadata-audio-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="metadata-audio-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="metadata-audio-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="metadata-audio-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="metadata-audio-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addmetadatatrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/audio.tracks.metadata}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/embed_media_modal_link
|
||||||
|
|
||||||
|
Embed media link modal template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<div class="tiny_mediacms_source {{id}}">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="source-url-input">
|
||||||
|
{{#str}} entersource, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="source-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepicker}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepicker}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label for="{{elementid}}_link_nameentry">{{#str}} entername, tiny_mediacms {{/str}}</label>
|
||||||
|
<input class="form-control fullwidth tiny_mediacms_name_entry" type="text" id="{{elementid}}_link_nameentry" size="32" required="true"/>
|
||||||
@@ -0,0 +1,832 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/embed_media_modal_video
|
||||||
|
|
||||||
|
Embed media video modal template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{{#video.sources}}
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_media_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="video-video-url-input">
|
||||||
|
{{#str}} videosourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="video-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{.}}"/>
|
||||||
|
{{#showfilepicker}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepicker}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addsource, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
{{#addsourcehelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/addsourcehelpicon}}
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.sources}}
|
||||||
|
{{^video}}
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_media_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="video-video-url-input">
|
||||||
|
{{#str}} videosourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="video-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepicker}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepicker}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addsource, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
{{#addsourcehelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/addsourcehelpicon}}
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video}}
|
||||||
|
<fieldset class="collapsible collapsed" id="{{elementid}}_video-display-options">
|
||||||
|
<input name="mform_isexpanded_{{elementid}}_video-display-options" type="hidden">
|
||||||
|
<legend class="d-flex align-items-center px-1">
|
||||||
|
<div class="position-relative d-flex ftoggler align-items-center position-relative me-1">
|
||||||
|
<a role="button" data-toggle="collapse" href="#vdisplayoptions" aria-expanded="false"
|
||||||
|
aria-controls="vdisplayoptions"
|
||||||
|
class="btn btn-icon me-3 icons-collapse-expand stretched-link fheader collapsed">
|
||||||
|
<span class="expanded-icon icon-no-margin p-2" title="{{#str}} collapse, moodle {{/str}}">
|
||||||
|
<i class="icon fa fa-chevron-down fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="collapsed-icon icon-no-margin p-2" title="{{#str}} expand, moodle {{/str}}">
|
||||||
|
<span class="dir-rtl-hide">
|
||||||
|
<i class="icon fa fa-chevron-right fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="dir-ltr-hide">
|
||||||
|
<i class="icon fa fa-chevron-left fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">{{#str}} displayoptions, tiny_mediacms {{/str}}</span>
|
||||||
|
</a>
|
||||||
|
<h3 class="d-flex align-self-stretch align-items-center mb-0" aria-hidden="true">
|
||||||
|
{{#str}} displayoptions, tiny_mediacms {{/str}}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</legend>
|
||||||
|
<div id="vdisplayoptions" class="fcontainer collapseable collapse px-1">
|
||||||
|
<div class="tiny_mediacms_display_options">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="vdisplayoptions_media-title-entry">{{#str}} entertitle, tiny_mediacms {{/str}}</label>
|
||||||
|
<input class="form-control fullwidth tiny_mediacms_title_entry" type="text" id="vdisplayoptions_media-title-entry"
|
||||||
|
size="32" value="{{video.title}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
<div class="mb-1">
|
||||||
|
<label>{{#str}} size, tiny_mediacms {{/str}}</label>
|
||||||
|
<div class="d-flex flex-wrap align-items-center tiny_mediacms_poster_size">
|
||||||
|
<label for="vdisplayoptions_media-width-entry" class="accesshide">{{#str}} videowidth, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="vdisplayoptions_media-width-entry" type="text" class="form-control w-auto me-1 tiny_mediacms_width_entry input-mini"
|
||||||
|
size="4" value="{{video.width}}"/>
|
||||||
|
x
|
||||||
|
<label for="vdisplayoptions_media-height-entry" class="accesshide">{{#str}} videoheight, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="vdisplayoptions_media-height-entry" type="text" class="form-control w-auto ms-1 tiny_mediacms_height_entry input-mini"
|
||||||
|
size="4" value="{{video.height}}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_poster_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="display-video-url-input">
|
||||||
|
{{#str}} entersource, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="display-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{video.poster}}"/>
|
||||||
|
{{#showfilepickerposter}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickerposter}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="collapsible collapsed" id="{{elementid}}_video-advanced-settings">
|
||||||
|
<input name="mform_isexpanded_{{elementid}}_video-advanced-settings" type="hidden">
|
||||||
|
{{renderPartial "form_components.section" context=this id="vadvancedsettings" name="advancedsettings"}}
|
||||||
|
<legend class="d-flex align-items-center px-1">
|
||||||
|
<div class="position-relative d-flex ftoggler align-items-center position-relative me-1">
|
||||||
|
<a role="button" data-toggle="collapse" href="#vadvancedsettings" aria-expanded="false"
|
||||||
|
aria-controls="vadvancedsettings"
|
||||||
|
class="btn btn-icon me-3 icons-collapse-expand stretched-link fheader collapsed">
|
||||||
|
<span class="expanded-icon icon-no-margin p-2" title="{{#str}} collapse, moodle {{/str}}">
|
||||||
|
<i class="icon fa fa-chevron-down fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="collapsed-icon icon-no-margin p-2" title="{{#str}} expand, moodle {{/str}}">
|
||||||
|
<span class="dir-rtl-hide">
|
||||||
|
<i class="icon fa fa-chevron-right fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="dir-ltr-hide">
|
||||||
|
<i class="icon fa fa-chevron-left fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">{{#str}} advancedsettings, tiny_mediacms {{/str}}</span>
|
||||||
|
</a>
|
||||||
|
<h3 class="d-flex align-self-stretch align-items-center mb-0" aria-hidden="true">
|
||||||
|
{{#str}} advancedsettings, tiny_mediacms {{/str}}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</legend>
|
||||||
|
<div id="vadvancedsettings" class="fcontainer collapseable collapse px-1">
|
||||||
|
<div class="tiny_mediacms_advancedsettings">
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" checked="true" class="form-check-input tiny_mediacms_controls"
|
||||||
|
id="vadvancedsettings_media-controls-toggle" {{# video.controls }}checked{{/ video.controls }}/>
|
||||||
|
<label class="form-check-label" for="vadvancedsettings_media-controls-toggle">
|
||||||
|
{{#str}} controls, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_autoplay"
|
||||||
|
id="vadvancedsettings_media-autoplay-toggle" {{# video.autoplay }}checked{{/ video.autoplay }}/>
|
||||||
|
<label class="form-check-label" for="vadvancedsettings_media-autoplay-toggle">
|
||||||
|
{{#str}} autoplay, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_mute"
|
||||||
|
id="vadvancedsettings_media-mute-toggle" {{# video.muted }}checked{{/ video.muted }}/>
|
||||||
|
<label class="form-check-label" for="vadvancedsettings_media-mute-toggle">
|
||||||
|
{{#str}} mute, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_loop"
|
||||||
|
id="vadvancedsettings_media-loop-toggle" {{# video.loop }}checked{{/ video.loop }}/>
|
||||||
|
<label class="form-check-label" for="vadvancedsettings_media-loop-toggle">
|
||||||
|
{{#str}} loop, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="collapsible collapsed" id="{{elementid}}_video-tracks">
|
||||||
|
<input name="mform_isexpanded_{{elementid}}_video-tracks" type="hidden">
|
||||||
|
<legend class="d-flex align-items-center px-1">
|
||||||
|
<div class="position-relative d-flex ftoggler align-items-center position-relative me-1">
|
||||||
|
<a role="button" data-toggle="collapse" href="#vtracks" aria-expanded="false"
|
||||||
|
aria-controls="vtracks"
|
||||||
|
class="btn btn-icon me-3 icons-collapse-expand stretched-link fheader collapsed">
|
||||||
|
<span class="expanded-icon icon-no-margin p-2" title="{{#str}} collapse, moodle {{/str}}">
|
||||||
|
<i class="icon fa fa-chevron-down fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="collapsed-icon icon-no-margin p-2" title="{{#str}} expand, moodle {{/str}}">
|
||||||
|
<span class="dir-rtl-hide">
|
||||||
|
<i class="icon fa fa-chevron-right fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span class="dir-ltr-hide">
|
||||||
|
<i class="icon fa fa-chevron-left fa-fw " aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">{{#str}} tracks, tiny_mediacms {{/str}}</span>
|
||||||
|
</a>
|
||||||
|
<h3 class="d-flex align-self-stretch align-items-center mb-0" aria-hidden="true">
|
||||||
|
{{#str}} tracks, tiny_mediacms {{/str}}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
{{#trackshelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/trackshelpicon}}
|
||||||
|
</legend>
|
||||||
|
<div id="vtracks" class="fcontainer collapseable collapse px-1">
|
||||||
|
<ul class="nav nav-tabs mb-3">
|
||||||
|
<li data-track-kind="subtitles" class="nav-item">
|
||||||
|
<a class="nav-link active" href="#{{elementid}}_vtracks_subtitles"
|
||||||
|
role="tab" data-toggle="tab">
|
||||||
|
{{#str}} subtitles, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-track-kind="captions" class="nav-item">
|
||||||
|
<a class="nav-link" href="#{{elementid}}_vtracks_captions" role="tab" data-toggle="tab">
|
||||||
|
{{#str}} captions, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-track-kind="descriptions" class="nav-item">
|
||||||
|
<a class="nav-link" href="#{{elementid}}_vtracks_descriptions"
|
||||||
|
role="tab" data-toggle="tab">
|
||||||
|
{{#str}} descriptions, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-track-kind="chapters" class="nav-item">
|
||||||
|
<a class="nav-link" href="#{{elementid}}_vtracks_chapters" role="tab" data-toggle="tab">
|
||||||
|
{{#str}} chapters, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-track-kind="metadata" class="nav-item">
|
||||||
|
<a class="nav-link" href="#{{elementid}}_vtracks_metadata" role="tab" data-toggle="tab">
|
||||||
|
{{#str}} metadata, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div data-track-kind="subtitles" class="tab-pane active"
|
||||||
|
id="{{elementid}}_vtracks_subtitles">
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#subtitleshelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/subtitleshelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#video.tracks.subtitles}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="subtitle-video-url-input">
|
||||||
|
{{#str}} subtitlessourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="subtitle-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="subtitle-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="subtitle-video-lang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="subtitle-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="subtitle-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addsubtitlestrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.subtitles}}
|
||||||
|
{{^video.tracks.subtitles}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="subtitle-video-url-input">
|
||||||
|
{{#str}} subtitlessourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="subtitle-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="subtitle-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="subtitle-video-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="subtitle-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="subtitle-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addsubtitlestrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.subtitles}}
|
||||||
|
</div>
|
||||||
|
<div data-track-kind="captions" class="tab-pane"
|
||||||
|
id="{{elementid}}_vtracks_captions">
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#captionshelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/captionshelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#video.tracks.captions}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="caption-video-url-input">
|
||||||
|
{{#str}} captionssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="caption-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="caption-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="caption-video-lang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="caption-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="caption-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addcaptionstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.captions}}
|
||||||
|
{{^video.tracks.captions}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="caption-video-url-input">
|
||||||
|
{{#str}} captionssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="caption-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="caption-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="caption-video-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="caption-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="caption-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addcaptionstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.captions}}
|
||||||
|
</div>
|
||||||
|
<div data-track-kind="descriptions" class="tab-pane"
|
||||||
|
id="{{elementid}}_vtracks_descriptions">
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#descriptionshelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/descriptionshelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#video.tracks.descriptions}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="description-video-url-input">
|
||||||
|
{{#str}} descriptionssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="description-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="description-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="description-video-ang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="description-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="description-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} adddescriptionstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.descriptions}}
|
||||||
|
{{^video.tracks.descriptions}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="description-video-url-input">
|
||||||
|
{{#str}} descriptionssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="description-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="description-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="description-video-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="description-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="description-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} adddescriptionstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.descriptions}}
|
||||||
|
</div>
|
||||||
|
<div data-track-kind="chapters" class="tab-pane"
|
||||||
|
id="{{elementid}}_vtracks_chapters">
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#chaptershelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/chaptershelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#video.tracks.chapters}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="chapter-video-url-input">
|
||||||
|
{{#str}} chapterssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="chapter-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="chapter-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="chapter-video-lang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="chapter-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="chapter-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addchapterstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.chapters}}
|
||||||
|
{{^video.tracks.chapters}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="chapter-video-url-input">
|
||||||
|
{{#str}} chapterssourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="chapter-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="chapter-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="chapter-video-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="chapter-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="chapter-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addchapterstrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.chapters}}
|
||||||
|
</div>
|
||||||
|
<div data-track-kind="metadata" class="tab-pane"
|
||||||
|
id="{{elementid}}_vtracks_metadata">
|
||||||
|
<div class="trackhelp">{{{helpStrings.metadata}}}</div>
|
||||||
|
<div class="trackhelp">
|
||||||
|
{{#metadatahelpicon}}
|
||||||
|
{{> core/help_icon }}
|
||||||
|
{{/metadatahelpicon}}
|
||||||
|
</div>
|
||||||
|
{{#video.tracks.metadata}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="metadata-video-url-input">
|
||||||
|
{{#str}} metadatasourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="metadata-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32" value="{{src}}"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="metadata-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="metadata-video-lang-input" class="custom-select tiny_mediacms_track_lang_entry" data-value="{{srclang}}">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="metadata-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="metadata-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text" value="{{label}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default" {{# defaultTrack }}checked{{/ defaultTrack }}/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addmetadatatrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.metadata}}
|
||||||
|
{{^video.tracks.metadata}}
|
||||||
|
<div class="mb-1 tiny_mediacms_track">
|
||||||
|
<div class="tiny_mediacms_source tiny_mediacms_track_source">
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="metadata-video-url-input">
|
||||||
|
{{#str}} metadatasourcelabel, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="input-group input-append w-100">
|
||||||
|
<input id="metadata-video-url-input" class="form-control tiny_mediacms_url_entry" type="url" size="32"/>
|
||||||
|
{{#showfilepickertrack}}
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button class="btn btn-secondary openmediacmsbrowser" type="button">{{#str}} browserepositories, tiny_mediacms {{/str}}</button>
|
||||||
|
</span>
|
||||||
|
{{/showfilepickertrack}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="metadata-video-lang-input">{{#str}} srclang, tiny_mediacms {{/str}}</label>
|
||||||
|
<select id="metadata-video-lang-input" class="custom-select tiny_mediacms_track_lang_entry">
|
||||||
|
<optgroup label="{{#str}} languagesinstalled, tiny_mediacms {{/str}}">
|
||||||
|
{{#langsinstalled}}
|
||||||
|
<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>
|
||||||
|
{{/langsinstalled}}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="{{#str}} languagesavailable, tiny_mediacms {{/str}} ">
|
||||||
|
{{#langsavailable}}
|
||||||
|
<option value="{{code}}">{{lang}}</option>
|
||||||
|
{{/langsavailable}}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="w-100" for="metadata-video-track-input">{{#str}} label, tiny_mediacms {{/str}}</label>
|
||||||
|
<input id="metadata-video-track-input" class="form-control tiny_mediacms_track_label_entry" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_mediacms_track_default"/>
|
||||||
|
<label class="form-check-label">{{#str}} default, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="addcomponent-wrapper">
|
||||||
|
<a href="#" class="addcomponent">
|
||||||
|
{{#str}} addmetadatatrack, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="removecomponent-wrapper hidden">
|
||||||
|
<a href="#" class="removecomponent">
|
||||||
|
{{#str}} remove, tiny_mediacms {{/str}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/video.tracks.metadata}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/embed_media_video
|
||||||
|
|
||||||
|
Embed media video template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<video{{!
|
||||||
|
}}{{#width}} width="{{.}}"{{/width}}{{!
|
||||||
|
}}{{#height}} height="{{.}}"{{/height}}{{!
|
||||||
|
}}{{#poster}} poster="{{.}}"{{/poster}}{{!
|
||||||
|
}}{{#showControls}} controls="true"{{/showControls}}{{!
|
||||||
|
}}{{#loop}} loop="true"{{/loop}}{{!
|
||||||
|
}}{{#muted}} muted="true"{{/muted}}{{!
|
||||||
|
}}{{#autoplay}} autoplay="true"{{/autoplay}}{{!
|
||||||
|
}}{{#title}} title="{{.}}"{{/title}}{{!
|
||||||
|
}}>
|
||||||
|
{{#sources}}
|
||||||
|
<source src="{{.}}">
|
||||||
|
{{/sources}}
|
||||||
|
{{#tracks}}
|
||||||
|
<track{{!
|
||||||
|
}} src="{{track}}"{{!
|
||||||
|
}} kind="{{kind}}"{{!
|
||||||
|
}} srclang="{{srclang}}"{{!
|
||||||
|
}} label="{{label}}"{{!
|
||||||
|
}}{{#defaultTrack}} default="true"{{/defaultTrack}}{{!
|
||||||
|
}}>
|
||||||
|
{{/tracks}}
|
||||||
|
{{#description}}{{.}}{{/description}}
|
||||||
|
</video>
|
||||||
134
lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_modal.mustache
Executable file
134
lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_modal.mustache
Executable file
@@ -0,0 +1,134 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/iframe_embed_modal
|
||||||
|
|
||||||
|
Iframe embed modal template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"elementid": "editor1",
|
||||||
|
"isupdating": false
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{{< core/modal }}
|
||||||
|
|
||||||
|
{{$body}}
|
||||||
|
<form class="tiny_iframecms_form" id="{{elementid}}_tiny_iframecms_form">
|
||||||
|
<!-- Tab Navigation -->
|
||||||
|
<ul class="nav nav-tabs mb-3 tiny_iframecms_tabs" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link active tiny_iframecms_tab_url_btn" id="{{elementid}}_tab_url"
|
||||||
|
data-bs-toggle="tab" data-bs-target="#{{elementid}}_pane_url"
|
||||||
|
type="button" role="tab" aria-controls="{{elementid}}_pane_url" aria-selected="true">
|
||||||
|
{{#str}} tabembedurl, tiny_mediacms {{/str}}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link tiny_iframecms_tab_iframe_library_btn" id="{{elementid}}_tab_iframe_library"
|
||||||
|
data-bs-toggle="tab" data-bs-target="#{{elementid}}_pane_iframe_library"
|
||||||
|
type="button" role="tab" aria-controls="{{elementid}}_pane_iframe_library" aria-selected="false">
|
||||||
|
{{#str}} tabvideolibraryiframe, tiny_mediacms {{/str}}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- Tab Content -->
|
||||||
|
<div class="tab-content">
|
||||||
|
<!-- Tab 1: Embed URL (existing content) -->
|
||||||
|
<div class="tab-pane fade show active tiny_iframecms_pane_url" id="{{elementid}}_pane_url" role="tabpanel" aria-labelledby="{{elementid}}_tab_url">
|
||||||
|
<div class="container-fluid p-0">
|
||||||
|
<div class="row">
|
||||||
|
<!-- Left column: URL and Options -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<!-- URL Input -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{elementid}}_iframe_url" class="form-label font-weight-bold">
|
||||||
|
{{#str}} iframeurl, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
class="form-control tiny_iframecms_url"
|
||||||
|
id="{{elementid}}_iframe_url"
|
||||||
|
rows="3"
|
||||||
|
placeholder="{{#str}} iframeurlplaceholder, tiny_mediacms {{/str}}"
|
||||||
|
>{{url}}</textarea>
|
||||||
|
<div class="tiny_iframecms_url_warning text-danger small mt-1 d-none">
|
||||||
|
{{#str}} iframeurlinvalid, tiny_mediacms {{/str}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{> tiny_mediacms/iframe_embed_options }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right column: Preview -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label font-weight-bold">
|
||||||
|
{{#str}} preview, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="tiny_iframecms_preview_container border rounded p-2 bg-light" style="min-height: 300px;">
|
||||||
|
<div class="tiny_iframecms_preview d-flex align-items-center justify-content-center text-muted" style="min-height: 280px;">
|
||||||
|
<span>{{#str}} iframeurlplaceholder, tiny_mediacms {{/str}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab 2: Video Library from Iframe -->
|
||||||
|
<div class="tab-pane fade tiny_iframecms_pane_iframe_library" id="{{elementid}}_pane_iframe_library" role="tabpanel" aria-labelledby="{{elementid}}_tab_iframe_library">
|
||||||
|
<div class="tiny_iframecms_iframe_library_container" style="min-height: 500px;">
|
||||||
|
<div class="tiny_iframecms_iframe_library_placeholder text-center py-5">
|
||||||
|
<p class="text-muted">{{#str}} libraryloading, tiny_mediacms {{/str}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="tiny_iframecms_iframe_library_loading text-center py-5 d-none">
|
||||||
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
<span class="sr-only visually-hidden">{{#str}} libraryloading, tiny_mediacms {{/str}}</span>
|
||||||
|
</div>
|
||||||
|
<p class="mt-2 text-muted">{{#str}} libraryloading, tiny_mediacms {{/str}}</p>
|
||||||
|
</div>
|
||||||
|
<iframe
|
||||||
|
class="tiny_iframecms_iframe_library_frame d-none"
|
||||||
|
src=""
|
||||||
|
style="width: 100%; height: 500px; border: 1px solid #dee2e6; border-radius: 0.25rem;"
|
||||||
|
frameborder="0"
|
||||||
|
allowfullscreen>
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{/body}}
|
||||||
|
|
||||||
|
{{$footer}}
|
||||||
|
<button type="button" class="btn btn-primary" data-action="save">
|
||||||
|
{{#isupdating}}
|
||||||
|
{{#str}} updateiframe, tiny_mediacms {{/str}}
|
||||||
|
{{/isupdating}}
|
||||||
|
{{^isupdating}}
|
||||||
|
{{#str}} insertiframe, tiny_mediacms {{/str}}
|
||||||
|
{{/isupdating}}
|
||||||
|
</button>
|
||||||
|
{{#isupdating}}
|
||||||
|
<button type="button" class="btn btn-danger tiny_iframecms_remove_btn" data-action="remove">
|
||||||
|
{{#str}} removeiframe, tiny_mediacms {{/str}}
|
||||||
|
</button>
|
||||||
|
{{/isupdating}}
|
||||||
|
<button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel, moodle {{/str}}</button>
|
||||||
|
{{/footer}}
|
||||||
|
|
||||||
|
{{/ core/modal }}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/iframe_embed_options
|
||||||
|
|
||||||
|
Embed options partial for iframe modal.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"elementid": "editor1",
|
||||||
|
"showTitle": true,
|
||||||
|
"linkTitle": true,
|
||||||
|
"showRelated": true,
|
||||||
|
"showUserAvatar": true,
|
||||||
|
"responsive": true,
|
||||||
|
"startAtEnabled": false,
|
||||||
|
"startAt": "0:00"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<!-- Embed Options -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label font-weight-bold">
|
||||||
|
{{#str}} embedoptions, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_iframecms_showtitle"
|
||||||
|
id="{{elementid}}_showtitle" {{#showTitle}}checked{{/showTitle}}>
|
||||||
|
<label class="form-check-label" for="{{elementid}}_showtitle">
|
||||||
|
{{#str}} showtitle, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_iframecms_linktitle"
|
||||||
|
id="{{elementid}}_linktitle" {{#linkTitle}}checked{{/linkTitle}}>
|
||||||
|
<label class="form-check-label" for="{{elementid}}_linktitle">
|
||||||
|
{{#str}} linktitle, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_iframecms_showrelated"
|
||||||
|
id="{{elementid}}_showrelated" {{#showRelated}}checked{{/showRelated}}>
|
||||||
|
<label class="form-check-label" for="{{elementid}}_showrelated">
|
||||||
|
{{#str}} showrelated, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_iframecms_showuseravatar"
|
||||||
|
id="{{elementid}}_showuseravatar" {{#showUserAvatar}}checked{{/showUserAvatar}}>
|
||||||
|
<label class="form-check-label" for="{{elementid}}_showuseravatar">
|
||||||
|
{{#str}} showuseravatar, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_iframecms_responsive"
|
||||||
|
id="{{elementid}}_responsive" {{#responsive}}checked{{/responsive}}>
|
||||||
|
<label class="form-check-label" for="{{elementid}}_responsive">
|
||||||
|
{{#str}} responsive, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-2 d-flex align-items-center">
|
||||||
|
<input type="checkbox" class="form-check-input tiny_iframecms_startat_enabled"
|
||||||
|
id="{{elementid}}_startat_enabled" {{#startAtEnabled}}checked{{/startAtEnabled}}>
|
||||||
|
<label class="form-check-label me-2" for="{{elementid}}_startat_enabled">
|
||||||
|
{{#str}} startat, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<input type="text" class="form-control form-control-sm tiny_iframecms_startat"
|
||||||
|
id="{{elementid}}_startat" value="{{startAt}}" placeholder="0:00" style="width: 70px;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Aspect Ratio -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{elementid}}_aspectratio" class="form-label font-weight-bold">
|
||||||
|
{{#str}} aspectratio, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<select class="form-control tiny_iframecms_aspectratio" id="{{elementid}}_aspectratio">
|
||||||
|
<option value="16:9" {{#is16_9}}selected{{/is16_9}}>{{#str}} aspectratio_16_9, tiny_mediacms {{/str}}</option>
|
||||||
|
<option value="4:3" {{#is4_3}}selected{{/is4_3}}>{{#str}} aspectratio_4_3, tiny_mediacms {{/str}}</option>
|
||||||
|
<option value="1:1" {{#is1_1}}selected{{/is1_1}}>{{#str}} aspectratio_1_1, tiny_mediacms {{/str}}</option>
|
||||||
|
<option value="custom" {{#isCustom}}selected{{/isCustom}}>{{#str}} aspectratio_custom, tiny_mediacms {{/str}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dimensions -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label font-weight-bold">
|
||||||
|
{{#str}} dimensions, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control tiny_iframecms_width"
|
||||||
|
id="{{elementid}}_width" value="{{width}}" placeholder="560">
|
||||||
|
<span class="input-group-text">px</span>
|
||||||
|
</div>
|
||||||
|
<small class="text-muted">{{#str}} width, tiny_mediacms {{/str}}</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control tiny_iframecms_height"
|
||||||
|
id="{{elementid}}_height" value="{{height}}" placeholder="315">
|
||||||
|
<span class="input-group-text">px</span>
|
||||||
|
</div>
|
||||||
|
<small class="text-muted">{{#str}} height, tiny_mediacms {{/str}}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/iframe_embed_output
|
||||||
|
|
||||||
|
Iframe embed output template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"src": "https://example.com/embed?m=abc123",
|
||||||
|
"width": 560,
|
||||||
|
"height": 315,
|
||||||
|
"responsive": true,
|
||||||
|
"aspectRatioClass": "ratio-16-9"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{{#responsive}}
|
||||||
|
<iframe src="{{src}}" style="width:100%;aspect-ratio:{{aspectRatioValue}};display:block;border:0;" allowFullScreen></iframe>
|
||||||
|
{{/responsive}}
|
||||||
|
{{^responsive}}
|
||||||
|
<iframe width="{{width}}" height="{{height}}" src="{{src}}" frameBorder="0" allowFullScreen></iframe>
|
||||||
|
{{/responsive}}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/iframe_video_library
|
||||||
|
|
||||||
|
Video library browser for iframe modal.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"elementid": "editor1"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<div class="tiny_iframecms_library_container">
|
||||||
|
<!-- Search and Filter Bar -->
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control tiny_iframecms_library_search"
|
||||||
|
placeholder="{{#str}} librarysearchplaceholder, tiny_mediacms {{/str}}">
|
||||||
|
<button type="button" class="btn btn-outline-secondary tiny_iframecms_library_search_btn">
|
||||||
|
<i class="fa fa-search" aria-hidden="true"></i>
|
||||||
|
{{#str}} search, moodle {{/str}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<select class="form-control tiny_iframecms_library_sort">
|
||||||
|
<option value="date_desc">{{#str}} librarysortnewest, tiny_mediacms {{/str}}</option>
|
||||||
|
<option value="date_asc">{{#str}} librarysortoldest, tiny_mediacms {{/str}}</option>
|
||||||
|
<option value="title_asc">{{#str}} librarysorttitle, tiny_mediacms {{/str}}</option>
|
||||||
|
<option value="views_desc">{{#str}} librarysortviews, tiny_mediacms {{/str}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Video Grid -->
|
||||||
|
<div class="tiny_iframecms_library_grid" style="max-height: 400px; overflow-y: auto;">
|
||||||
|
<!-- Loading state -->
|
||||||
|
<div class="tiny_iframecms_library_loading text-center py-5">
|
||||||
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
<span class="sr-only">{{#str}} loading, tiny_mediacms {{/str}}</span>
|
||||||
|
</div>
|
||||||
|
<p class="mt-2 text-muted">{{#str}} libraryloading, tiny_mediacms {{/str}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Videos will be rendered here -->
|
||||||
|
<div class="tiny_iframecms_library_items row d-none">
|
||||||
|
<!-- Video items will be dynamically inserted here -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Empty state -->
|
||||||
|
<div class="tiny_iframecms_library_empty text-center py-5 d-none">
|
||||||
|
<i class="fa fa-video-camera fa-3x text-muted mb-3" aria-hidden="true"></i>
|
||||||
|
<p class="text-muted">{{#str}} libraryempty, tiny_mediacms {{/str}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Error state -->
|
||||||
|
<div class="tiny_iframecms_library_error text-center py-5 d-none">
|
||||||
|
<i class="fa fa-exclamation-triangle fa-3x text-warning mb-3" aria-hidden="true"></i>
|
||||||
|
<p class="text-danger tiny_iframecms_library_error_message">{{#str}} libraryerror, tiny_mediacms {{/str}}</p>
|
||||||
|
<button type="button" class="btn btn-secondary tiny_iframecms_library_retry">
|
||||||
|
{{#str}} libraryretry, tiny_mediacms {{/str}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div class="tiny_iframecms_library_pagination d-flex justify-content-between align-items-center mt-3 d-none">
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm tiny_iframecms_library_prev" disabled>
|
||||||
|
<i class="fa fa-chevron-left" aria-hidden="true"></i> {{#str}} libraryprev, tiny_mediacms {{/str}}
|
||||||
|
</button>
|
||||||
|
<span class="tiny_iframecms_library_page_info text-muted"></span>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm tiny_iframecms_library_next">
|
||||||
|
{{#str}} librarynext, tiny_mediacms {{/str}} <i class="fa fa-chevron-right" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/iframe_video_library_item
|
||||||
|
|
||||||
|
Single video item in the library grid.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"id": "abc123",
|
||||||
|
"title": "My Video",
|
||||||
|
"thumbnail": "https://example.com/thumb.jpg",
|
||||||
|
"duration": "2:30",
|
||||||
|
"views": "150 views",
|
||||||
|
"date": "2 weeks ago",
|
||||||
|
"embedUrl": "https://example.com/embed?m=abc123"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<div class="col-md-4 col-sm-6 mb-3">
|
||||||
|
<div class="tiny_iframecms_library_item card h-100"
|
||||||
|
data-video-id="{{id}}"
|
||||||
|
data-embed-url="{{embedUrl}}"
|
||||||
|
style="cursor: pointer;">
|
||||||
|
<div class="position-relative">
|
||||||
|
<img src="{{thumbnail}}" class="card-img-top" alt="{{title}}"
|
||||||
|
style="height: 120px; object-fit: cover;"
|
||||||
|
onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22320%22 height=%22120%22><rect fill=%22%23ddd%22 width=%22320%22 height=%22120%22/><text x=%2250%%22 y=%2250%%22 dominant-baseline=%22middle%22 text-anchor=%22middle%22 fill=%22%23999%22 font-size=%2214%22>No thumbnail</text></svg>'">
|
||||||
|
{{#duration}}
|
||||||
|
<span class="position-absolute bottom-0 end-0 bg-dark text-white px-1 m-1 small rounded"
|
||||||
|
style="font-size: 0.75rem;">
|
||||||
|
{{duration}}
|
||||||
|
</span>
|
||||||
|
{{/duration}}
|
||||||
|
<!-- Select overlay button -->
|
||||||
|
<div class="tiny_iframecms_library_item_overlay position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center"
|
||||||
|
style="background: rgba(0,0,0,0.5); opacity: 0; transition: opacity 0.2s;">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm tiny_iframecms_library_select_btn">
|
||||||
|
{{#str}} libraryselect, tiny_mediacms {{/str}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-2">
|
||||||
|
<h6 class="card-title mb-1 text-truncate" title="{{title}}" style="font-size: 0.875rem;">
|
||||||
|
{{title}}
|
||||||
|
</h6>
|
||||||
|
<p class="card-text text-muted mb-0" style="font-size: 0.75rem;">
|
||||||
|
{{#views}}{{views}} • {{/views}}{{date}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.tiny_iframecms_library_item:hover .tiny_iframecms_library_item_overlay {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
.tiny_iframecms_library_item:hover {
|
||||||
|
box-shadow: 0 0.25rem 0.5rem rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
34
lms-plugins/mediacms-moodle/tiny/mediacms/templates/image.mustache
Executable file
34
lms-plugins/mediacms-moodle/tiny/mediacms/templates/image.mustache
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/image
|
||||||
|
|
||||||
|
Image template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<img src="{{url}}" alt="{{alt}}"{{!
|
||||||
|
}}{{#width}} width="{{.}}"{{/width}}{{!
|
||||||
|
}}{{#height}} height="{{.}}"{{/height}}{{!
|
||||||
|
}}{{#presentation}} role="presentation"{{/presentation}}{{!
|
||||||
|
}}{{#customStyle}} style="{{.}}"{{/customStyle}}{{!
|
||||||
|
}}{{#classlist}} class="{{.}}"{{/classlist}}{{!
|
||||||
|
}}{{#id}} id="{{.}}"{{/id}}{{!
|
||||||
|
}}/>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/insert_image_modal
|
||||||
|
|
||||||
|
Insert image template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"uniqid": 0,
|
||||||
|
"elementid": "exampleId",
|
||||||
|
"loading": {},
|
||||||
|
"title": "Insert image"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{{< core/modal }}
|
||||||
|
{{$body}}
|
||||||
|
<form class="tiny_imagecms_form">
|
||||||
|
<!-- URL warning -->
|
||||||
|
<div role="alert" class="d-none alert alert-warning mb-1 tiny_imagecms_urlwarning">
|
||||||
|
<label>
|
||||||
|
{{#str}} imageurlrequired, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<!-- Presentation warning -->
|
||||||
|
<div role="alert" class="d-none tiny_imagecms_altwarning alert alert-warning mb-1">
|
||||||
|
<label>
|
||||||
|
{{#str}} presentationoraltrequired, tiny_mediacms {{/str}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<!-- Preloader icon -->
|
||||||
|
<div role="progressbar" class="d-none tiny_imagecms_loader lead border rounded text-center">
|
||||||
|
<div class="tiny_imagecms_loader_container d-flex flex-column justify-content-center">
|
||||||
|
<span data-region="loading-icon-container">
|
||||||
|
{{> core/loading }}
|
||||||
|
</span>
|
||||||
|
<div>{{#str}} loading, tiny_mediacms {{/str}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tiny_imagecms_body_template"></div>
|
||||||
|
</form>
|
||||||
|
{{/body}}
|
||||||
|
|
||||||
|
{{$footer}}
|
||||||
|
<div class="tiny_imagecms_footer_template container"></div>
|
||||||
|
{{/footer}}
|
||||||
|
{{/ core/modal }}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/insert_image_modal_details
|
||||||
|
|
||||||
|
Insert image details body template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"elementid": "exampleId",
|
||||||
|
"alt": "Image description",
|
||||||
|
"presentation": true,
|
||||||
|
"width": 600,
|
||||||
|
"height": 400,
|
||||||
|
"customStyle": "",
|
||||||
|
"sizecustomhelpicon": {
|
||||||
|
"text": "Help text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
<div class="tiny_imagecms_image_details">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<!-- Column 1: Image Preview and Description -->
|
||||||
|
<div class="tiny_imagecms_preview_col col-lg-7 p-0">
|
||||||
|
<input type="hidden" class="tiny_imagecms_customstyle" value="{{customStyle}}">
|
||||||
|
<!-- Row 1: Image preview -->
|
||||||
|
<div class="tiny_imagecms_preview_box border rounded">
|
||||||
|
<!-- Delete image icon -->
|
||||||
|
<div class="tiny_imagecms_deleteicon" tabindex="0" title="{{#str}} deleteimage, tiny_mediacms {{/str}}">
|
||||||
|
<i class="fa fa-trash-o" title="{{#str}} deleteimage, tiny_mediacms {{/str}}"></i>
|
||||||
|
</div>
|
||||||
|
<!-- Image placeholder -->
|
||||||
|
<img class="tiny_imagecms_preview" src="data:," alt>
|
||||||
|
</div>
|
||||||
|
<!-- Row 2: Image description -->
|
||||||
|
<div class="form-group mt-3">
|
||||||
|
<label for="{{elementid}}_tiny_imagecms_altentry">{{#str}} enteralt, tiny_mediacms {{/str}}</label>
|
||||||
|
<textarea class="tiny_imagecms_altentry form-control fullwidth" id="{{elementid}}_tiny_imagecms_altentry" name="altentry" maxlength="125">{{alt}}</textarea>
|
||||||
|
<!-- Character counter -->
|
||||||
|
<div id="the-count" class="d-flex justify-content-end small">
|
||||||
|
<span id="currentcount">0</span>
|
||||||
|
<span id="maximumcount"> / 125</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Column 2: Checkbox and Radio Buttons -->
|
||||||
|
<div class="tiny_imagecms_properties_col col-lg-5">
|
||||||
|
<!-- Row 1: Image presentation role -->
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input type="checkbox" class="tiny_imagecms_presentation form-check-input" id="{{elementid}}_tiny_imagecms_presentation" {{# presentation }}checked{{/ presentation }}>
|
||||||
|
<label class="form-check-label" for="{{elementid}}_tiny_imagecms_presentation">{{#str}} presentation, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<!-- Row 2: Original size radiobutton -->
|
||||||
|
<div class="form-check mb-2 ps-0">
|
||||||
|
<input type="radio" class="tiny_imagecms_sizeoriginal" id="{{elementid}}_tiny_imagecms_sizeoriginal" name="radioOptions">
|
||||||
|
<label class="form-check-label" for="{{elementid}}_tiny_imagecms_sizeoriginal">{{#str}} sizeoriginal, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<!-- Row 3: Custom size radiobutton -->
|
||||||
|
<div class="form-check ps-0 mb-2">
|
||||||
|
<input type="radio" class="tiny_imagecms_sizecustom" id="{{elementid}}_tiny_imagecms_sizecustom" name="radioOptions">
|
||||||
|
<label class="form-check-label" for="{{elementid}}_tiny_imagecms_sizecustom">{{#str}} sizecustom, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
<!-- Row 4: Image size -->
|
||||||
|
<div class="tiny_imagecms_properties mb-2">
|
||||||
|
<!-- Row 1: Image width and height -->
|
||||||
|
<div id="{{elementid}}_tiny_imagecms_size" class="tiny_imagecms_size container ms-1">
|
||||||
|
<div class="d-flex justify-content-start">
|
||||||
|
<!-- Column 1: Width Input -->
|
||||||
|
<div class="flex-item me-2">
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<input type="number" min="0" class="tiny_imagecms_widthentry form-control me-1 input-mini" id="{{elementid}}_tiny_imagecms_widthentry" value="{{width}}">
|
||||||
|
<label for="{{elementid}}_tiny_imagecms_widthentry" class="ms-1">{{#str}} width, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Column 2: "X" Text -->
|
||||||
|
<div class="flex-item me-1 mt-2">X</div>
|
||||||
|
<!-- Column 3: Height Input -->
|
||||||
|
<div class="flex-item me-1">
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<input type="number" min="0" class="tiny_imagecms_heightentry form-control ms-1 input-mini" id="{{elementid}}_tiny_imagecms_heightentry" value="{{height}}">
|
||||||
|
<label for="{{elementid}}_tiny_imagecms_heightentry" class="ms-1">{{#str}} height, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tiny_imagecms_customhelpicon flex-item ms-1">{{#sizecustomhelpicon}}{{> core/help_icon }}{{/sizecustomhelpicon}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Row 2: Keep proportion -->
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input type="checkbox" class="tiny_imagecms_constrain form-check-input" id="{{elementid}}_tiny_imagecms_constrain">
|
||||||
|
<label class="form-check-label" for="{{elementid}}_tiny_imagecms_constrain">{{#str}} constrain, tiny_mediacms {{/str}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/insert_image_modal_details_footer
|
||||||
|
|
||||||
|
Insert image details footer template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"elementid": "exampleId"
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
<div class="row">
|
||||||
|
<!-- First Column -->
|
||||||
|
<div class="col-md-6 d-flex align-items-center p-0">
|
||||||
|
<!-- Row 1: URL related label -->
|
||||||
|
<span class="tiny_imagecms_filename text-truncate me-1"></span>
|
||||||
|
</div>
|
||||||
|
<!-- Column 2: Saving, canceling, browsing repositories buttons -->
|
||||||
|
<div class="col-md-6 text-end mt-2 md-0 p-0">
|
||||||
|
<!-- Row 1: Cancel button -->
|
||||||
|
<button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel, moodle {{/str}}</button>
|
||||||
|
<!-- Row 2: Save button -->
|
||||||
|
<button class="tiny_imagecms_urlentrysubmit btn btn-primary" type="submit">{{#str}} saveimage, tiny_mediacms {{/str}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/insert_image_modal_insert
|
||||||
|
|
||||||
|
Insert image body template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"showdropzone": true,
|
||||||
|
"showfilepicker": true
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
<div class="tiny_imagecms_insert_image">
|
||||||
|
<div class="tiny_imagecms_dropzone d-flex flex-column justify-content-center">
|
||||||
|
{{#showdropzone}}
|
||||||
|
<div class="tiny_imagecms_dropzone_container"></div>
|
||||||
|
<!-- File input -->
|
||||||
|
<input type="file" id="tiny_imagecms_fileinput" accept="image/*" class="d-none">
|
||||||
|
{{/showdropzone}}
|
||||||
|
{{^showdropzone}}
|
||||||
|
<div class="text-center">
|
||||||
|
<!-- Dropzone icon -->
|
||||||
|
<i class="fa-6x pb-2 text-secondary fa fa-cloud"></i>
|
||||||
|
<!-- Dropzone string -->
|
||||||
|
<div class="lead text-center">
|
||||||
|
<p class="mb-0">
|
||||||
|
{{#showfilepicker}}
|
||||||
|
{{#str}} repositoryuploadnotpermitted, tiny_mediacms {{/str}}</p>
|
||||||
|
{{/showfilepicker}}
|
||||||
|
{{^showfilepicker}}
|
||||||
|
{{#str}} repositorynotpermitted, tiny_mediacms {{/str}}
|
||||||
|
{{/showfilepicker}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/showdropzone}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/insert_image_modal_insert_footer
|
||||||
|
|
||||||
|
Insert image footer template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"showdropzone": true,
|
||||||
|
"elementid": "exampleId",
|
||||||
|
"src": "https://moodle.org/logo.png",
|
||||||
|
"showfilepicker": true
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
<div class="row">
|
||||||
|
<!-- First Column -->
|
||||||
|
<div class="col-md-6 d-flex align-items-center p-0">
|
||||||
|
<!-- Row 1: URL related label -->
|
||||||
|
{{#showdropzone}}
|
||||||
|
<label for="{{elementid}}_tiny_imagecms_urlentry" class="tiny_imagecms_url_label me-1">{{#str}} enterurlor, tiny_mediacms {{/str}}</label>
|
||||||
|
{{/showdropzone}}
|
||||||
|
{{^showdropzone}}
|
||||||
|
<label for="{{elementid}}_tiny_imagecms_urlentry" class="tiny_imagecms_url_label me-1">{{#str}} enterurl, tiny_mediacms {{/str}}</label>
|
||||||
|
{{/showdropzone}}
|
||||||
|
<!-- Row 2: URL entry input. Needed by the insert image step and image details step if the image URL source from external -->
|
||||||
|
<input name="urlentry" class="tiny_imagecms_urlentry form-control w-50 me-1" type="url" id="{{elementid}}_tiny_imagecms_urlentry" size="32" value="{{src}}">
|
||||||
|
<!-- Row 3: Add button. Needed by the insert image step -->
|
||||||
|
<button disabled class="tiny_imagecms_addurl btn btn-secondary me-1" type="button">{{#str}} addurl, tiny_mediacms {{/str}}</button>
|
||||||
|
</div>
|
||||||
|
<!-- Column 2: Saving, canceling, browsing repositories buttons -->
|
||||||
|
<div class="col-md-6 text-end mt-2 md-0 p-0">
|
||||||
|
<!-- Row 1: Cancel button -->
|
||||||
|
<button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel, moodle {{/str}}</button>
|
||||||
|
{{#showfilepicker}}
|
||||||
|
<!-- Row 3: Browse repositories button -->
|
||||||
|
<button class="openimagecmsbrowser btn btn-secondary" type="button">{{#str}} browserepositoriesimage, tiny_mediacms {{/str}}</button>
|
||||||
|
{{/showfilepicker}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
34
lms-plugins/mediacms-moodle/tiny/mediacms/templates/missingfiles.mustache
Executable file
34
lms-plugins/mediacms-moodle/tiny/mediacms/templates/missingfiles.mustache
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/missingfiles
|
||||||
|
|
||||||
|
Insert image template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
"missingFiles": [
|
||||||
|
"Example.png",
|
||||||
|
"Another.mov"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<ol>
|
||||||
|
{{#missingFiles}}
|
||||||
|
<li>{{.}}</li>
|
||||||
|
{{/missingFiles}}
|
||||||
|
</ol>
|
||||||
28
lms-plugins/mediacms-moodle/tiny/mediacms/templates/mm2_iframe.mustache
Executable file
28
lms-plugins/mediacms-moodle/tiny/mediacms/templates/mm2_iframe.mustache
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
{{!
|
||||||
|
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/>.
|
||||||
|
}}
|
||||||
|
{{!
|
||||||
|
@template tiny_mediacms/mm2_iframe
|
||||||
|
|
||||||
|
Insert image template.
|
||||||
|
|
||||||
|
Example context (json):
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<iframe class="mmcms_iframe" id="mmcms-iframe" src={{src}}>
|
||||||
|
</iframe>
|
||||||
74
lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/image.feature
Executable file
74
lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/image.feature
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
@editor @editor_tiny @tiny_mediacms @javascript
|
||||||
|
Feature: Use the TinyMCE editor to upload an image
|
||||||
|
In order to work with images
|
||||||
|
As a user
|
||||||
|
I need to be able to upload and manipulate images
|
||||||
|
|
||||||
|
Scenario: Clicking on the Image button in the TinyMCE editor opens the image dialog
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
When I click on the "Image" button for the "Description" TinyMCE editor
|
||||||
|
Then "Insert image" "dialogue" should exist
|
||||||
|
|
||||||
|
Scenario: Browsing repositories in the TinyMCE editor opens the image dialog and shows the FilePicker
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
When I click on the "Image" button for the "Description" TinyMCE editor
|
||||||
|
And I click on "Browse repositories" "button" in the "Insert image" "dialogue"
|
||||||
|
Then "File picker" "dialogue" should exist
|
||||||
|
|
||||||
|
Scenario: Focus returns to the correct location after closing a nested FilePicker
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
When I click on the "Image" button for the "Description" TinyMCE editor
|
||||||
|
And I press "Browse repositories"
|
||||||
|
When I press the escape key
|
||||||
|
Then the focused element is "Browse repositories" "button"
|
||||||
|
|
||||||
|
@_file_upload @test_tiny
|
||||||
|
Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker and upload url image
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
When I click on the "Image" button for the "Description" TinyMCE editor
|
||||||
|
And I click on "Browse repositories" "button" in the "Insert image" "dialogue"
|
||||||
|
And I upload "/lib/editor/tiny/tests/behat/fixtures/tinyscreenshot.png" to the file picker for TinyMCE
|
||||||
|
# Note: This needs to be replaced with a label.
|
||||||
|
Then ".tiny_image_preview" "css_element" should be visible
|
||||||
|
|
||||||
|
@_file_upload
|
||||||
|
Scenario: Insert image to the TinyMCE editor
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
And I click on the "Image" button for the "Description" TinyMCE editor
|
||||||
|
And I click on "Browse repositories" "button" in the "Insert image" "dialogue"
|
||||||
|
And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE
|
||||||
|
And I set the field "How would you describe this image to someone who can't see it?" to "It's the Moodle"
|
||||||
|
And I click on "Save" "button" in the "Image details" "dialogue"
|
||||||
|
When I select the "img" element in position "0" of the "Description" TinyMCE editor
|
||||||
|
And I click on the "Image" button for the "Description" TinyMCE editor
|
||||||
|
Then the field "How would you describe this image to someone who can't see it?" matches value "It's the Moodle"
|
||||||
|
# Note: This needs to be replaced with a label.
|
||||||
|
And ".tiny_image_preview" "css_element" should be visible
|
||||||
|
|
||||||
|
@_file_upload
|
||||||
|
Scenario: Resizing the image uses the original and custom sizes and the keep proportion checkbox
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
And I click on the "Image" button for the "Description" TinyMCE editor
|
||||||
|
And I click on "Browse repositories" "button" in the "Insert image" "dialogue"
|
||||||
|
And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE
|
||||||
|
And I click on "This image is decorative only" "checkbox"
|
||||||
|
And I click on "Save" "button" in the "Image details" "dialogue"
|
||||||
|
When I select the "img" element in position "0" of the "Description" TinyMCE editor
|
||||||
|
And I click on the "Image" button for the "Description" TinyMCE editor
|
||||||
|
Then the field "Original size" matches value "1"
|
||||||
|
And I click on "Custom size" "radio"
|
||||||
|
Then the field "Keep proportion" matches value "1"
|
||||||
|
And I click on "Keep proportion" "checkbox"
|
||||||
|
And I set the field "Width" to "102"
|
||||||
|
And I click on "Save" "button" in the "Image details" "dialogue"
|
||||||
|
When I select the "img" element in position "0" of the "Description" TinyMCE editor
|
||||||
|
And I click on the "Image" button for the "Description" TinyMCE editor
|
||||||
|
Then the field "Custom size" matches value "1"
|
||||||
|
And the field "Width" matches value "102"
|
||||||
|
And the field "Keep proportion" matches value "0"
|
||||||
58
lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/video.feature
Executable file
58
lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/video.feature
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
@editor @editor_tiny @tiny_mediacms @javascript
|
||||||
|
Feature: Use the TinyMCE editor to upload a video
|
||||||
|
In order to work with videos
|
||||||
|
As a user
|
||||||
|
I need to be able to upload and manipulate videos
|
||||||
|
|
||||||
|
Scenario: Clicking on the Video button in the TinyMCE editor opens the video dialog
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
When I click on the "Multimedia" button for the "Description" TinyMCE editor
|
||||||
|
Then "Insert media" "dialogue" should exist
|
||||||
|
|
||||||
|
Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
When I click on the "Multimedia" button for the "Description" TinyMCE editor
|
||||||
|
And I click on "Browse repositories" "button" in the "Insert media" "dialogue"
|
||||||
|
Then "File picker" "dialogue" should exist
|
||||||
|
|
||||||
|
@_file_upload
|
||||||
|
Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
When I click on the "Multimedia" button for the "Description" TinyMCE editor
|
||||||
|
And I follow "Video"
|
||||||
|
And I click on "Browse repositories..." "button" in the "#id_description_editor_video .tiny_media_source.tiny_media_media_source" "css_element"
|
||||||
|
And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE
|
||||||
|
When I click on "Insert media" "button"
|
||||||
|
And I select the "video" element in position "1" of the "Description" TinyMCE editor
|
||||||
|
|
||||||
|
@_file_upload
|
||||||
|
Scenario: Insert and update video in the TinyMCE editor
|
||||||
|
Given I log in as "admin"
|
||||||
|
And I open my profile in edit mode
|
||||||
|
And I click on the "Multimedia" button for the "Description" TinyMCE editor
|
||||||
|
And I follow "Video"
|
||||||
|
And I click on "Browse repositories..." "button" in the "#id_description_editor_video .tiny_media_source.tiny_media_media_source" "css_element"
|
||||||
|
And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE
|
||||||
|
And I click on "Insert media" "button"
|
||||||
|
And I select the "video" element in position "1" of the "Description" TinyMCE editor
|
||||||
|
When I click on the "Multimedia" button for the "Description" TinyMCE editor
|
||||||
|
And I click on "Display options" "link"
|
||||||
|
And I set the field "Title" to "Test title"
|
||||||
|
And I click on "Advanced settings" "link"
|
||||||
|
And I click on "Play automatically" "checkbox"
|
||||||
|
And I click on "Muted" "checkbox"
|
||||||
|
And I click on "Loop" "checkbox"
|
||||||
|
Then "Insert media" "button" should not exist in the "Insert media" "dialogue"
|
||||||
|
And "Update media" "button" should exist in the "Insert media" "dialogue"
|
||||||
|
And I click on "Update media" "button"
|
||||||
|
And I select the "video" element in position "1" of the "Description" TinyMCE editor
|
||||||
|
And I click on the "Multimedia" button for the "Description" TinyMCE editor
|
||||||
|
And I click on "Display options" "link"
|
||||||
|
And the field "Title" matches value "Test title"
|
||||||
|
And I click on "Advanced settings" "link"
|
||||||
|
And the field "Play automatically" matches value "1"
|
||||||
|
And the field "Muted" matches value "1"
|
||||||
|
And the field "Loop" matches value "1"
|
||||||
30
lms-plugins/mediacms-moodle/tiny/mediacms/version.php
Executable file
30
lms-plugins/mediacms-moodle/tiny/mediacms/version.php
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiny media plugin version details.
|
||||||
|
*
|
||||||
|
* @package tiny_media
|
||||||
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
$plugin->version = 2026020102; // Bumped version to ensure upgrade
|
||||||
|
$plugin->requires = 2024100100;
|
||||||
|
$plugin->component = 'tiny_mediacms';
|
||||||
|
$plugin->dependencies = ['filter_mediacms' => 2026020100]; // Keep dependency on our filter
|
||||||
Reference in New Issue
Block a user