mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-02-04 06:22:59 -05:00
all
This commit is contained in:
@@ -1 +1 @@
|
||||
VERSION = "8.08"
|
||||
VERSION = "8.09"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { translateString, inEmbeddedApp } from '../utils/helpers/';
|
||||
import { translateString, inSelectMediaEmbedMode } from '../utils/helpers/';
|
||||
|
||||
interface MediaListHeaderProps {
|
||||
title?: string;
|
||||
@@ -11,12 +11,12 @@ interface MediaListHeaderProps {
|
||||
|
||||
export const MediaListHeader: React.FC<MediaListHeaderProps> = (props) => {
|
||||
const viewAllText = props.viewAllText || translateString('VIEW ALL');
|
||||
const isEmbedMode = inEmbeddedApp();
|
||||
const isSelectMediaMode = inSelectMediaEmbedMode();
|
||||
|
||||
return (
|
||||
<div className={(props.className ? props.className + ' ' : '') + 'media-list-header'} style={props.style}>
|
||||
<h2>{props.title}</h2>
|
||||
{!isEmbedMode && props.viewAllLink ? (
|
||||
{!isSelectMediaMode && props.viewAllLink ? (
|
||||
<h3>
|
||||
{' '}
|
||||
<a href={props.viewAllLink} title={viewAllText}>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useMediaItem } from '../../utils/hooks/';
|
||||
import { PositiveInteger, PositiveIntegerOrZero, inEmbeddedApp } from '../../utils/helpers/';
|
||||
import { PositiveInteger, PositiveIntegerOrZero, inSelectMediaEmbedMode } from '../../utils/helpers/';
|
||||
import { MediaItemThumbnailLink, itemClassname } from './includes/items/';
|
||||
import { Item } from './Item';
|
||||
|
||||
export function MediaItem(props) {
|
||||
const type = props.type;
|
||||
const isEmbedMode = inEmbeddedApp();
|
||||
const isSelectMediaMode = inSelectMediaEmbedMode();
|
||||
|
||||
const [titleComponentOrig, descriptionComponent, thumbnailUrl, UnderThumbWrapperOrig, editMediaComponent, metaComponents, viewMediaComponent] =
|
||||
useMediaItem({ ...props, type });
|
||||
@@ -21,14 +21,14 @@ export function MediaItem(props) {
|
||||
|
||||
const ItemMain = ({ children }) => <div className="item-main">{children}</div>;
|
||||
|
||||
const titleComponent = isEmbedMode
|
||||
const titleComponent = isSelectMediaMode
|
||||
? () => <ItemTitle title={props.title} />
|
||||
: titleComponentOrig;
|
||||
|
||||
const UnderThumbWrapper = isEmbedMode ? ItemMain : UnderThumbWrapperOrig;
|
||||
const UnderThumbWrapper = isSelectMediaMode ? ItemMain : UnderThumbWrapperOrig;
|
||||
|
||||
function thumbnailComponent() {
|
||||
if (isEmbedMode) {
|
||||
if (isSelectMediaMode) {
|
||||
// In embed mode, render thumbnail without link
|
||||
const thumbStyle = thumbnailUrl ? { backgroundImage: "url('" + thumbnailUrl + "')" } : null;
|
||||
return (
|
||||
@@ -57,13 +57,13 @@ export function MediaItem(props) {
|
||||
const finalClassname = containerClassname +
|
||||
(props.showSelection ? ' with-selection' : '') +
|
||||
(props.isSelected ? ' selected' : '') +
|
||||
(props.hasAnySelection || isEmbedMode ? ' has-any-selection' : '');
|
||||
(props.hasAnySelection || isSelectMediaMode ? ' has-any-selection' : '');
|
||||
|
||||
const handleItemClick = (e) => {
|
||||
const isEmbedMode = inEmbeddedApp();
|
||||
const isSelectMediaMode = inSelectMediaEmbedMode();
|
||||
|
||||
// In embed mode or if there's any selection active, clicking the item should toggle selection
|
||||
if ((isEmbedMode || props.hasAnySelection) && props.onCheckboxChange) {
|
||||
// In select media mode or if there's any selection active, clicking the item should toggle selection
|
||||
if ((isSelectMediaMode || props.hasAnySelection) && props.onCheckboxChange) {
|
||||
// Check if clicking on the checkbox itself, edit icon, or view icon
|
||||
if (e.target.closest('.item-selection-checkbox') ||
|
||||
e.target.closest('.item-edit-icon') ||
|
||||
@@ -93,12 +93,12 @@ export function MediaItem(props) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isEmbedMode && editMediaComponent()}
|
||||
{!isEmbedMode && viewMediaComponent()}
|
||||
{!isSelectMediaMode && editMediaComponent()}
|
||||
{!isSelectMediaMode && viewMediaComponent()}
|
||||
|
||||
{thumbnailComponent()}
|
||||
|
||||
{isEmbedMode ? (
|
||||
{isSelectMediaMode ? (
|
||||
<UnderThumbWrapper>
|
||||
{titleComponent()}
|
||||
{metaComponents()}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useMediaItem } from '../../utils/hooks/';
|
||||
import { PositiveIntegerOrZero, inEmbeddedApp } from '../../utils/helpers/';
|
||||
import { PositiveIntegerOrZero, inSelectMediaEmbedMode } from '../../utils/helpers/';
|
||||
import { MediaDurationInfo } from '../../utils/classes/';
|
||||
import { MediaPlaylistOptions } from '../media-playlist-options/MediaPlaylistOptions';
|
||||
import { MediaItemDuration, MediaItemPlaylistIndex, itemClassname } from './includes/items/';
|
||||
@@ -9,7 +9,7 @@ import { MediaItem } from './MediaItem';
|
||||
|
||||
export function MediaItemAudio(props) {
|
||||
const type = props.type;
|
||||
const isEmbedMode = inEmbeddedApp();
|
||||
const isSelectMediaMode = inSelectMediaEmbedMode();
|
||||
|
||||
const [titleComponentOrig, descriptionComponent, thumbnailUrl, UnderThumbWrapperOrig, editMediaComponent, metaComponents, viewMediaComponent] =
|
||||
useMediaItem({ ...props, type });
|
||||
@@ -23,11 +23,11 @@ export function MediaItemAudio(props) {
|
||||
|
||||
const ItemMain = ({ children }) => <div className="item-main">{children}</div>;
|
||||
|
||||
const titleComponent = isEmbedMode
|
||||
const titleComponent = isSelectMediaMode
|
||||
? () => <ItemTitle title={props.title} />
|
||||
: titleComponentOrig;
|
||||
|
||||
const UnderThumbWrapper = isEmbedMode ? ItemMain : UnderThumbWrapperOrig;
|
||||
const UnderThumbWrapper = isSelectMediaMode ? ItemMain : UnderThumbWrapperOrig;
|
||||
|
||||
const _MediaDurationInfo = new MediaDurationInfo();
|
||||
|
||||
@@ -38,7 +38,7 @@ export function MediaItemAudio(props) {
|
||||
const durationISO8601 = _MediaDurationInfo.ISO8601();
|
||||
|
||||
function thumbnailComponent() {
|
||||
if (isEmbedMode) {
|
||||
if (isSelectMediaMode) {
|
||||
// In embed mode, render thumbnail without link
|
||||
return (
|
||||
<div
|
||||
@@ -99,11 +99,11 @@ export function MediaItemAudio(props) {
|
||||
const finalClassname = containerClassname +
|
||||
(props.showSelection ? ' with-selection' : '') +
|
||||
(props.isSelected ? ' selected' : '') +
|
||||
(props.hasAnySelection || isEmbedMode ? ' has-any-selection' : '');
|
||||
(props.hasAnySelection || isSelectMediaMode ? ' has-any-selection' : '');
|
||||
|
||||
const handleItemClick = (e) => {
|
||||
// In embed mode or if there's any selection active, clicking the item should toggle selection
|
||||
if ((isEmbedMode || props.hasAnySelection) && props.onCheckboxChange) {
|
||||
if ((isSelectMediaMode || props.hasAnySelection) && props.onCheckboxChange) {
|
||||
// Check if clicking on the checkbox itself, edit icon, or view icon
|
||||
if (e.target.closest('.item-selection-checkbox') ||
|
||||
e.target.closest('.item-edit-icon') ||
|
||||
@@ -135,12 +135,12 @@ export function MediaItemAudio(props) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isEmbedMode && editMediaComponent()}
|
||||
{!isEmbedMode && viewMediaComponent()}
|
||||
{!isSelectMediaMode && editMediaComponent()}
|
||||
{!isSelectMediaMode && viewMediaComponent()}
|
||||
|
||||
{thumbnailComponent()}
|
||||
|
||||
{isEmbedMode ? (
|
||||
{isSelectMediaMode ? (
|
||||
<UnderThumbWrapper>
|
||||
{titleComponent()}
|
||||
{metaComponents()}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useMediaItem } from '../../utils/hooks/';
|
||||
import { PositiveIntegerOrZero, inEmbeddedApp } from '../../utils/helpers/';
|
||||
import { PositiveIntegerOrZero, inSelectMediaEmbedMode } from '../../utils/helpers/';
|
||||
import { MediaDurationInfo } from '../../utils/classes/';
|
||||
import { MediaPlaylistOptions } from '../media-playlist-options/MediaPlaylistOptions.jsx';
|
||||
import { MediaItemVideoPlayer, MediaItemDuration, MediaItemVideoPreviewer, MediaItemPlaylistIndex, itemClassname } from './includes/items/';
|
||||
@@ -9,7 +9,7 @@ import { MediaItem } from './MediaItem';
|
||||
|
||||
export function MediaItemVideo(props) {
|
||||
const type = props.type;
|
||||
const isEmbedMode = inEmbeddedApp();
|
||||
const isSelectMediaMode = inSelectMediaEmbedMode();
|
||||
|
||||
const [titleComponentOrig, descriptionComponent, thumbnailUrl, UnderThumbWrapperOrig, editMediaComponent, metaComponents, viewMediaComponent] =
|
||||
useMediaItem({ ...props, type });
|
||||
@@ -23,11 +23,11 @@ export function MediaItemVideo(props) {
|
||||
|
||||
const ItemMain = ({ children }) => <div className="item-main">{children}</div>;
|
||||
|
||||
const titleComponent = isEmbedMode
|
||||
const titleComponent = isSelectMediaMode
|
||||
? () => <ItemTitle title={props.title} />
|
||||
: titleComponentOrig;
|
||||
|
||||
const UnderThumbWrapper = isEmbedMode ? ItemMain : UnderThumbWrapperOrig;
|
||||
const UnderThumbWrapper = isSelectMediaMode ? ItemMain : UnderThumbWrapperOrig;
|
||||
|
||||
const _MediaDurationInfo = new MediaDurationInfo();
|
||||
|
||||
@@ -42,8 +42,8 @@ export function MediaItemVideo(props) {
|
||||
}
|
||||
|
||||
function thumbnailComponent() {
|
||||
if (isEmbedMode) {
|
||||
// In embed mode, render thumbnail without link
|
||||
if (isSelectMediaMode) {
|
||||
// In select media mode, render thumbnail without link
|
||||
return (
|
||||
<div
|
||||
key="item-thumb"
|
||||
@@ -109,11 +109,11 @@ export function MediaItemVideo(props) {
|
||||
const finalClassname = containerClassname +
|
||||
(props.showSelection ? ' with-selection' : '') +
|
||||
(props.isSelected ? ' selected' : '') +
|
||||
(props.hasAnySelection || isEmbedMode ? ' has-any-selection' : '');
|
||||
(props.hasAnySelection || isSelectMediaMode ? ' has-any-selection' : '');
|
||||
|
||||
const handleItemClick = (e) => {
|
||||
// In embed mode or if there's any selection active, clicking the item should toggle selection
|
||||
if ((isEmbedMode || props.hasAnySelection) && props.onCheckboxChange) {
|
||||
// In select media mode or if there's any selection active, clicking the item should toggle selection
|
||||
if ((isSelectMediaMode || props.hasAnySelection) && props.onCheckboxChange) {
|
||||
// Check if clicking on the checkbox itself, edit icon, or view icon
|
||||
if (e.target.closest('.item-selection-checkbox') ||
|
||||
e.target.closest('.item-edit-icon') ||
|
||||
@@ -145,12 +145,12 @@ export function MediaItemVideo(props) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isEmbedMode && editMediaComponent()}
|
||||
{!isEmbedMode && viewMediaComponent()}
|
||||
{!isSelectMediaMode && editMediaComponent()}
|
||||
{!isSelectMediaMode && viewMediaComponent()}
|
||||
|
||||
{props.hasMediaViewer ? videoViewerComponent() : thumbnailComponent()}
|
||||
|
||||
{isEmbedMode ? (
|
||||
{isSelectMediaMode ? (
|
||||
<UnderThumbWrapper>
|
||||
{titleComponent()}
|
||||
{metaComponents()}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { LinksContext, MemberContext, SiteContext } from '../../utils/contexts/'
|
||||
import { PageStore, ProfilePageStore } from '../../utils/stores/';
|
||||
import { PageActions, ProfilePageActions } from '../../utils/actions/';
|
||||
import { CircleIconButton, PopupMain } from '../_shared';
|
||||
import { translateString, inEmbeddedApp } from '../../utils/helpers/';
|
||||
import { translateString, inEmbeddedApp, inSelectMediaEmbedMode } from '../../utils/helpers/';
|
||||
|
||||
class ProfileSearchBar extends React.PureComponent {
|
||||
constructor(props) {
|
||||
@@ -372,7 +372,7 @@ class NavMenuInlineTabs extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const isEmbedMode = inEmbeddedApp();
|
||||
const isSelectMediaMode = inSelectMediaEmbedMode();
|
||||
|
||||
return (
|
||||
<nav ref="tabsNav" className="profile-nav items-list-outer list-inline list-slider">
|
||||
@@ -380,7 +380,7 @@ class NavMenuInlineTabs extends React.PureComponent {
|
||||
{this.state.displayPrev ? this.previousBtn : null}
|
||||
|
||||
<ul className="items-list-wrap" ref="itemsListWrap">
|
||||
{!isEmbedMode ? (
|
||||
{!isSelectMediaMode ? (
|
||||
<InlineTab
|
||||
id="about"
|
||||
isActive={'about' === this.props.type}
|
||||
@@ -411,7 +411,7 @@ class NavMenuInlineTabs extends React.PureComponent {
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{!isEmbedMode && MemberContext._currentValue.can.saveMedia ? (
|
||||
{!isSelectMediaMode && MemberContext._currentValue.can.saveMedia ? (
|
||||
<InlineTab
|
||||
id="playlists"
|
||||
isActive={'playlists' === this.props.type}
|
||||
@@ -772,7 +772,7 @@ export default function ProfilePagesHeader(props) {
|
||||
)}
|
||||
|
||||
<div className="profile-info-nav-wrap">
|
||||
{inEmbeddedApp() ? (
|
||||
{inSelectMediaEmbedMode() ? (
|
||||
<div className="profile-info">
|
||||
<div className="profile-info-inner">
|
||||
<div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { ApiUrlContext, LinksConsumer, MemberContext } from '../utils/contexts';
|
||||
import { PageStore, ProfilePageStore } from '../utils/stores';
|
||||
import { ProfilePageActions, PageActions } from '../utils/actions';
|
||||
import { inEmbeddedApp, translateString } from '../utils/helpers/';
|
||||
import { inEmbeddedApp, inSelectMediaEmbedMode, translateString } from '../utils/helpers/';
|
||||
import { MediaListWrapper } from '../components/MediaListWrapper';
|
||||
import ProfilePagesHeader from '../components/profile-page/ProfilePagesHeader';
|
||||
import ProfilePagesContent from '../components/profile-page/ProfilePagesContent';
|
||||
@@ -202,13 +202,13 @@ export class ProfileMediaPage extends Page {
|
||||
}
|
||||
|
||||
handleMediaSelection(mediaId, isSelected) {
|
||||
const isEmbedMode = inEmbeddedApp();
|
||||
const isSelectMediaMode = inSelectMediaEmbedMode();
|
||||
|
||||
this.setState((prevState) => {
|
||||
const newSelectedMedia = new Set();
|
||||
|
||||
// In embed mode, only allow single selection
|
||||
if (isEmbedMode) {
|
||||
// In select media mode, only allow single selection
|
||||
if (isSelectMediaMode) {
|
||||
if (isSelected) {
|
||||
newSelectedMedia.add(mediaId);
|
||||
console.log('Selected media item:', mediaId);
|
||||
@@ -933,7 +933,7 @@ export class ProfileMediaPage extends Page {
|
||||
const authorData = ProfilePageStore.get('author-data');
|
||||
|
||||
const isMediaAuthor = authorData && authorData.username === MemberContext._currentValue.username;
|
||||
const isEmbedMode = inEmbeddedApp();
|
||||
const isSelectMediaMode = inSelectMediaEmbedMode();
|
||||
|
||||
// Check if any filters are active (excluding default sort and tags)
|
||||
const hasActiveFilters =
|
||||
@@ -965,16 +965,16 @@ export class ProfileMediaPage extends Page {
|
||||
this.state.author ? (
|
||||
<ProfilePagesContent key="ProfilePagesContent">
|
||||
<MediaListWrapper
|
||||
title={isEmbedMode ? undefined : this.state.title}
|
||||
title={isSelectMediaMode ? undefined : this.state.title}
|
||||
className="items-list-ver"
|
||||
style={isEmbedMode ? { marginTop: '24px' } : undefined}
|
||||
showBulkActions={!isEmbedMode && isMediaAuthor}
|
||||
style={isSelectMediaMode ? { marginTop: '24px' } : undefined}
|
||||
showBulkActions={!isSelectMediaMode && isMediaAuthor}
|
||||
selectedCount={this.state.selectedMedia.size}
|
||||
totalCount={this.state.availableMediaIds.length}
|
||||
onBulkAction={this.handleBulkAction}
|
||||
onSelectAll={this.handleSelectAll}
|
||||
onDeselectAll={this.handleDeselectAll}
|
||||
showAddMediaButton={!isEmbedMode && isMediaAuthor}
|
||||
showAddMediaButton={!isSelectMediaMode && isMediaAuthor}
|
||||
>
|
||||
<ProfileMediaFilters
|
||||
hidden={this.state.hiddenFilters}
|
||||
@@ -997,7 +997,7 @@ export class ProfileMediaPage extends Page {
|
||||
hideViews={!PageStore.get('config-media-item').displayViews}
|
||||
hideDate={!PageStore.get('config-media-item').displayPublishDate}
|
||||
canEdit={isMediaAuthor}
|
||||
showSelection={isMediaAuthor || isEmbedMode}
|
||||
showSelection={isMediaAuthor || isSelectMediaMode}
|
||||
hasAnySelection={this.state.selectedMedia.size > 0}
|
||||
selectedMedia={this.state.selectedMedia}
|
||||
onMediaSelection={this.handleMediaSelection}
|
||||
|
||||
@@ -18,3 +18,29 @@ export function inEmbeddedApp() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isSelectMediaMode() {
|
||||
try {
|
||||
const params = new URL(globalThis.location.href).searchParams;
|
||||
const action = params.get('action');
|
||||
|
||||
if (action === 'select_media') {
|
||||
sessionStorage.setItem('media_cms_select_media', 'true');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear if action is explicitly something else
|
||||
if (action && action !== 'select_media') {
|
||||
sessionStorage.removeItem('media_cms_select_media');
|
||||
return false;
|
||||
}
|
||||
|
||||
return sessionStorage.getItem('media_cms_select_media') === 'true';
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function inSelectMediaEmbedMode() {
|
||||
return inEmbeddedApp() && isSelectMediaMode();
|
||||
}
|
||||
|
||||
@@ -4,10 +4,38 @@ namespace tiny_mediacms;
|
||||
|
||||
use context;
|
||||
use moodle_url;
|
||||
use editor_tiny\plugin;
|
||||
use editor_tiny\plugin_with_buttons;
|
||||
use editor_tiny\plugin_with_menuitems;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
class plugininfo extends \editor_tiny\plugin {
|
||||
class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menuitems {
|
||||
|
||||
/**
|
||||
* Whether the plugin is enabled
|
||||
*/
|
||||
public static function is_enabled(context $context, array $options, array $fpoptions, ?\editor_tiny\editor $editor = null): bool {
|
||||
return isloggedin() && !isguestuser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available buttons
|
||||
*/
|
||||
public static function get_available_buttons(): array {
|
||||
return [
|
||||
'tiny_mediacms/tiny_mediacms',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available menu items
|
||||
*/
|
||||
public static function get_available_menuitems(): array {
|
||||
return [
|
||||
'tiny_mediacms/tiny_mediacms',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin configuration for the editor.
|
||||
|
||||
@@ -36,7 +36,7 @@ class SelectMediaView(View):
|
||||
|
||||
def get(self, request):
|
||||
"""Display media selection interface - redirects to user's profile page"""
|
||||
profile_url = f"/user/{request.user.username}?mode=embed_mode"
|
||||
profile_url = f"/user/{request.user.username}?mode=embed_mode&action=select_media"
|
||||
return HttpResponseRedirect(profile_url)
|
||||
|
||||
@method_decorator(csrf_exempt)
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user