Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4 features to review in this PR #213

Merged
merged 7 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/css/edit.scss
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,9 @@ p.snippet-scope, .snippet-scope p {
.wrap .notice {
scroll-margin: 0.75em;
}

#edit-snippet-form-container .cs-sticky-notice {
position: sticky;
top: 40px;
z-index: 100;
}
14 changes: 6 additions & 8 deletions src/css/manage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ $inactive-color: #ccc;
height: 17px;
border-radius: 34px;
background-color: #ccc;
box-shadow: 0 0px 1px 0px rgba(0, 0, 0, .2);
transition: all 200ms ease-in-out !important;

&:hover {
box-shadow: 0 1px 3px 0px rgba(0, 0, 0, 0.5);
}

&::before {
transition: all .4s;
Expand All @@ -72,20 +78,12 @@ $inactive-color: #ccc;
border-radius: 50%;
}

&:hover::before {
transform: translateX(40%);
}

.snippets .active-snippet & {
background-color: $active-color;

&::before {
transform: translateX(100%);
}

&:hover::before {
transform: translateX(60%);
}
}

.snippets .erroneous-snippet &::before {
Expand Down
2 changes: 2 additions & 0 deletions src/js/components/SnippetForm/fields/ScopeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { buildShortcodeTag } from '../../../utils/shortcodes'
import { getSnippetType } from '../../../utils/snippets'
import { CopyToClipboardButton } from '../../common/CopyToClipboardButton'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
import { truncateWords } from '../../../utils/text'
import type { ShortcodeAtts } from '../../../utils/shortcodes'
import type { SnippetScope } from '../../../types/Snippet'
import type { Dispatch, SetStateAction} from 'react'
Expand Down Expand Up @@ -117,6 +118,7 @@ const ShortcodeInfo: React.FC = () => {
<>
<ShortcodeTag atts={{
id: snippet.id,
name: truncateWords(snippet.name),
network: snippet.network ?? isNetworkAdmin(),
...options
}} />
Expand Down
20 changes: 12 additions & 8 deletions src/js/components/SnippetForm/page/Notices.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,31 @@ import classnames from 'classnames'
import React, { useEffect } from 'react'
import { __, sprintf } from '@wordpress/i18n'
import { useSnippetForm } from '../../../hooks/useSnippetForm'
import type { MouseEventHandler, ReactNode} from 'react'
import type { ReactNode } from 'react'

interface DismissibleNoticeProps {
classNames?: classnames.Argument
onRemove: MouseEventHandler<HTMLButtonElement>
onRemove: VoidFunction
children?: ReactNode
autoHide?: boolean
}

const DismissibleNotice: React.FC<DismissibleNoticeProps> = ({ classNames, onRemove, children }) => {
const DismissibleNotice: React.FC<DismissibleNoticeProps> = ({ classNames, onRemove, children, autoHide = true }) => {
useEffect(() => {
if (window.CODE_SNIPPETS_EDIT?.scrollToNotices) {
window.scrollTo({ top: 0, behavior: 'smooth' })
if (autoHide) {
const timer = setTimeout(onRemove, 5000)
return () => clearTimeout(timer)
}
}, [])
return undefined
}, [autoHide, onRemove])

return (
<div id="message" className={classnames('notice fade is-dismissible', classNames)}>
<div id="message" className={classnames('cs-sticky-notice notice fade is-dismissible', classNames)}>
<>{children}</>

<button type="button" className="notice-dismiss" onClick={event => {
event.preventDefault()
onRemove(event)
onRemove()
}}>
<span className="screen-reader-text">{__('Dismiss notice.', 'code-snippets')}</span>
</button>
Expand All @@ -45,6 +48,7 @@ export const Notices: React.FC = () => {
<DismissibleNotice
classNames="error"
onRemove={() => setSnippet(previous => ({ ...previous, code_error: null }))}
autoHide={false}
>
<p>
<strong>{sprintf(
Expand Down
1 change: 1 addition & 0 deletions src/js/types/Shortcodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface SourceShortcodeAtts {

export interface ContentShortcodeAtts {
id: string
name: string
php: boolean
format: boolean
shortcodes: boolean
Expand Down
7 changes: 7 additions & 0 deletions src/js/utils/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,10 @@ export const trimLeadingChar = (text: string, character: string): string =>

export const trimTrailingChar = (text: string, character: string): string =>
character === text.charAt(text.length - 1) ? text.slice(0, -1) : text

export const truncateWords = (text: string, wordCount: number = 3): string => {
const words = text.trim().split(/\s+/);
return words.length > wordCount
? `${words.slice(0, wordCount).join(' ')}...`
: text;
};
28 changes: 25 additions & 3 deletions src/php/front-end/class-front-end.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ class Front_End {
*/
const PRISM_HANDLE = 'code-snippets-prism';

/**
* Maximum depth for shortcode recursion.
*/
const MAX_SHORTCODE_DEPTH = 5;

/**
* Class constructor
*/
Expand Down Expand Up @@ -235,7 +240,7 @@ protected function convert_boolean_attribute_flags( array $atts, array $boolean_
*
* @return string Evaluated shortcode content.
*/
protected function evaluate_shortcode_content( Snippet $snippet, array $atts ): string {
protected function evaluate_shortcode_content( Snippet $snippet, array $atts ): string {
if ( empty( $atts['php'] ) ) {
return $snippet->code;
}
Expand Down Expand Up @@ -325,8 +330,25 @@ public function render_content_shortcode( array $atts ): string {
// Remove this shortcode from the list to prevent recursion.
remove_shortcode( self::CONTENT_SHORTCODE );

// Evaluate shortcodes.
$content = do_shortcode( $atts['format'] ? shortcode_unautop( $content ) : $content );
// Recursion depth is limited to prevent infinite loops.
static $depth = 0;
$max_depth = self::MAX_SHORTCODE_DEPTH;

// Find the shortcode in the content and replace it with the evaluated content.
$content = preg_replace_callback(
'/\[' . self::CONTENT_SHORTCODE . '([^\]]*)\]/',
function ($matches) use (&$depth, $max_depth) {
if ($depth >= $max_depth) {
return '<!-- Max shortcode depth reached -->';
}
$depth++;
$atts = shortcode_parse_atts($matches[1]);
$result = $this->render_content_shortcode($atts);
$depth--;
return $result;
},
$content
);

// Add this shortcode back to the list.
add_shortcode( self::CONTENT_SHORTCODE, [ $this, 'render_content_shortcode' ] );
Expand Down
Loading