mirror of
https://github.com/TryGhost/Ghost.git
synced 2023-12-13 21:00:40 +01:00
✨ Added Source
as the new default theme
refs TryGhost/Product#3510 - Added `TryGhost/Source` as a submodule in `ghost/core/content/themes` so `Source` will ship with Ghost (along with Casper) - With this change, new installs will use `Source` as the default theme. Existing sites will have `Source` installed, but not activated, as this is a large change and we don't want to drastically change existing sites without warning. Users can upgrade to use `Source` simply by clicking 'Activate' in design settings. - Updated protections to prevent users from uploading their own conflicting version of `Source`
This commit is contained in:
parent
add7f1283b
commit
80a6fe17d0
82 changed files with 1294 additions and 124 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -97,6 +97,7 @@ typings/
|
|||
/ghost/core/content/adapters/storage/**/*
|
||||
/ghost/core/content/adapters/scheduling/**/*
|
||||
/ghost/core/content/themes/casper
|
||||
/ghost/core/content/themes/source
|
||||
!/ghost/core/README.md
|
||||
!/ghost/core/content/**/README.md
|
||||
|
||||
|
|
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -2,3 +2,7 @@
|
|||
path = ghost/core/content/themes/casper
|
||||
url = ../../TryGhost/Casper.git
|
||||
ignore = all
|
||||
[submodule "ghost/core/content/themes/source"]
|
||||
path = ghost/core/content/themes/source
|
||||
url = ../../TryGhost/Source.git
|
||||
ignore = all
|
||||
|
|
|
@ -133,9 +133,13 @@ export function isActiveTheme(theme: Theme): boolean {
|
|||
}
|
||||
|
||||
export function isDefaultTheme(theme: Theme): boolean {
|
||||
return theme.name === 'source';
|
||||
}
|
||||
|
||||
export function isLegacyTheme(theme: Theme): boolean {
|
||||
return theme.name === 'casper';
|
||||
}
|
||||
|
||||
export function isDeletableTheme(theme: Theme): boolean {
|
||||
return !isDefaultTheme(theme) && !isActiveTheme(theme);
|
||||
return !isDefaultTheme(theme) && !isLegacyTheme(theme) && !isActiveTheme(theme);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage';
|
|||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import React from 'react';
|
||||
import useHandleError from '../../../../utils/api/handleError';
|
||||
import {Theme, isActiveTheme, isDefaultTheme, isDeletableTheme, useActivateTheme, useDeleteTheme} from '../../../../api/themes';
|
||||
import {Theme, isActiveTheme, isDefaultTheme, isDeletableTheme, isLegacyTheme, useActivateTheme, useDeleteTheme} from '../../../../api/themes';
|
||||
import {downloadFile, getGhostPaths} from '../../../../utils/helpers';
|
||||
|
||||
interface ThemeActionProps {
|
||||
|
@ -23,6 +23,8 @@ function getThemeLabel(theme: Theme): React.ReactNode {
|
|||
|
||||
if (isDefaultTheme(theme)) {
|
||||
label += ' (default)';
|
||||
} else if (isLegacyTheme(theme)) {
|
||||
label += ' (legacy)';
|
||||
} else if (theme.package?.name !== theme.name) {
|
||||
label =
|
||||
<span className='text-sm md:text-base'>
|
||||
|
|
|
@ -4,6 +4,13 @@ import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage';
|
|||
import React from 'react';
|
||||
import {OfficialTheme, useOfficialThemes} from '../../../providers/ServiceProvider';
|
||||
import {getGhostPaths, resolveAsset} from '../../../../utils/helpers';
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
const sourceDemos = [
|
||||
{image: 'Source.png', category: 'News'},
|
||||
{image: 'Source-Magazine.png', category: 'Magazine'},
|
||||
{image: 'Source-Newsletter.png', category: 'Newsletter'}
|
||||
];
|
||||
|
||||
const OfficialThemes: React.FC<{
|
||||
onSelectTheme?: (theme: OfficialTheme) => void;
|
||||
|
@ -12,6 +19,20 @@ const OfficialThemes: React.FC<{
|
|||
}) => {
|
||||
const {adminRoot} = getGhostPaths();
|
||||
const officialThemes = useOfficialThemes();
|
||||
const [currentSourceDemoIndex, setCurrentSourceDemoIndex] = useState(0);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
if (isHovered) {
|
||||
setCurrentSourceDemoIndex(prevIndex => (prevIndex + 1) % sourceDemos.length);
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [isHovered]);
|
||||
|
||||
return (
|
||||
<ModalPage heading='Themes'>
|
||||
|
@ -22,16 +43,33 @@ const OfficialThemes: React.FC<{
|
|||
onSelectTheme?.(theme);
|
||||
}}>
|
||||
{/* <img alt={theme.name} src={`${assetRoot}/${theme.image}`}/> */}
|
||||
<div className='w-full bg-grey-100 shadow-md transition-all duration-500 hover:scale-[1.05]'>
|
||||
<img
|
||||
alt={`${theme.name} Theme`}
|
||||
className='h-full w-full object-contain'
|
||||
src={resolveAsset(theme.image, adminRoot)}
|
||||
/>
|
||||
<div className='relative w-full bg-grey-100 shadow-md transition-all duration-500 hover:scale-[1.05]' onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
|
||||
{theme.name !== 'Source' ?
|
||||
<img
|
||||
alt={`${theme.name} Theme`}
|
||||
className='h-full w-full object-contain'
|
||||
src={resolveAsset(theme.image, adminRoot)}
|
||||
/> :
|
||||
<>
|
||||
{sourceDemos.map((demo, index) => (
|
||||
<img
|
||||
key={`source-theme-${demo.category}`}
|
||||
alt={`${theme.name} Theme - ${demo.category}`}
|
||||
className={`${index === 0 ? 'relative' : 'absolute'} left-0 top-0 h-full w-full object-contain transition-opacity duration-500 ${index === currentSourceDemoIndex ? 'opacity-100' : 'opacity-0'}`}
|
||||
src={resolveAsset(`assets/img/themes/${demo.image}`, adminRoot)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<div className='mt-3'>
|
||||
<div className='relative mt-3'>
|
||||
<Heading level={4}>{theme.name}</Heading>
|
||||
<span className='text-sm text-grey-700'>{theme.category}</span>
|
||||
{theme.name !== 'Source' ?
|
||||
<span className='text-sm text-grey-700'>{theme.category}</span> :
|
||||
sourceDemos.map((demo, index) => (
|
||||
<span className={`${index === 0 ? 'relative' : 'absolute bottom-[1px]'} left-0 inline-block w-24 bg-white text-sm text-grey-700 ${index === currentSourceDemoIndex ? 'opacity-100' : 'opacity-0'}`}>{demo.category}</span>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
|
|
@ -7,9 +7,16 @@ import MobileChrome from '../../../../admin-x-ds/global/chrome/MobileChrome';
|
|||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import PageHeader from '../../../../admin-x-ds/global/layout/PageHeader';
|
||||
import React, {useState} from 'react';
|
||||
import Select, {SelectOption} from '../../../../admin-x-ds/global/form/Select';
|
||||
import {OfficialTheme} from '../../../providers/ServiceProvider';
|
||||
import {Theme} from '../../../../api/themes';
|
||||
|
||||
const sourceDemos = [
|
||||
{label: 'News', value: 'news', url: 'https://source.ghost.io'},
|
||||
{label: 'Magazine', value: 'magazine', url: 'https://source-magazine.ghost.io'},
|
||||
{label: 'Newsletter', value: 'newsletter', url: 'https://source-newsletter.ghost.io'}
|
||||
];
|
||||
|
||||
const ThemePreview: React.FC<{
|
||||
selectedTheme?: OfficialTheme;
|
||||
isInstalling?: boolean;
|
||||
|
@ -26,6 +33,7 @@ const ThemePreview: React.FC<{
|
|||
onInstall
|
||||
}) => {
|
||||
const [previewMode, setPreviewMode] = useState('desktop');
|
||||
const [currentSourceDemo, setCurrentSourceDemo] = useState<SelectOption>(sourceDemos[0]);
|
||||
|
||||
if (!selectedTheme) {
|
||||
return null;
|
||||
|
@ -68,6 +76,7 @@ const ThemePreview: React.FC<{
|
|||
<div className='flex items-center gap-2'>
|
||||
<Breadcrumbs
|
||||
activeItemClassName='hidden md:!block md:!visible'
|
||||
containerClassName='whitespace-nowrap'
|
||||
itemClassName='hidden md:!block md:!visible'
|
||||
items={[
|
||||
{label: 'Design', onClick: onClose},
|
||||
|
@ -78,6 +87,24 @@ const ThemePreview: React.FC<{
|
|||
backIcon
|
||||
onBack={onBack}
|
||||
/>
|
||||
{selectedTheme.name === 'Source' ?
|
||||
<>
|
||||
<span className='hidden md:!visible md:!block'>–</span>
|
||||
<Select
|
||||
border={false}
|
||||
containerClassName='text-sm font-bold'
|
||||
controlClasses={{menu: 'w-24'}}
|
||||
fullWidth={false}
|
||||
options={sourceDemos}
|
||||
selectedOption={currentSourceDemo}
|
||||
onSelect={(option) => {
|
||||
if (option) {
|
||||
setCurrentSourceDemo(option);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</> : null
|
||||
}
|
||||
</div>;
|
||||
|
||||
const right =
|
||||
|
@ -118,13 +145,19 @@ const ThemePreview: React.FC<{
|
|||
<div className='flex h-[calc(100%-74px)] grow flex-col items-center justify-center bg-grey-50 dark:bg-black'>
|
||||
{previewMode === 'desktop' ?
|
||||
<DesktopChrome>
|
||||
<iframe className='h-full w-full'
|
||||
src={selectedTheme?.previewUrl} title='Theme preview' />
|
||||
<iframe
|
||||
className='h-full w-full'
|
||||
src={selectedTheme.name !== 'Source' ? selectedTheme?.previewUrl : sourceDemos.find(demo => demo.label === currentSourceDemo.label)?.url}
|
||||
title='Theme preview'
|
||||
/>
|
||||
</DesktopChrome>
|
||||
:
|
||||
<MobileChrome>
|
||||
<iframe className='h-full w-full'
|
||||
src={selectedTheme?.previewUrl} title='Theme preview' />
|
||||
<iframe
|
||||
className='h-full w-full'
|
||||
src={selectedTheme.name !== 'Source' ? selectedTheme?.previewUrl : sourceDemos.find(demo => demo.label === currentSourceDemo.label)?.url}
|
||||
title='Theme preview'
|
||||
/>
|
||||
</MobileChrome>
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -13,6 +13,12 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
|||
}}
|
||||
ghostVersion='5.x'
|
||||
officialThemes={[{
|
||||
name: 'Source',
|
||||
category: 'News',
|
||||
previewUrl: 'https://source.ghost.io/',
|
||||
ref: 'default',
|
||||
image: 'assets/img/themes/Source.png'
|
||||
}, {
|
||||
name: 'Casper',
|
||||
category: 'Blog',
|
||||
previewUrl: 'https://demo.ghost.io/',
|
||||
|
|
|
@ -12,18 +12,17 @@ import {tracked} from '@glimmer/tracking';
|
|||
|
||||
// TODO: Long term move asset management directly in AdminX
|
||||
const officialThemes = [{
|
||||
name: 'Source',
|
||||
category: 'News',
|
||||
previewUrl: 'https://source.ghost.io/',
|
||||
ref: 'default',
|
||||
image: 'assets/img/themes/Source.png'
|
||||
}, {
|
||||
name: 'Casper',
|
||||
category: 'Blog',
|
||||
previewUrl: 'https://demo.ghost.io/',
|
||||
ref: 'default',
|
||||
ref: 'TryGhost/Casper',
|
||||
image: 'assets/img/themes/Casper.png'
|
||||
}, {
|
||||
name: 'Headline',
|
||||
category: 'News',
|
||||
url: 'https://github.com/TryGhost/Headline',
|
||||
previewUrl: 'https://headline.ghost.io',
|
||||
ref: 'TryGhost/Headline',
|
||||
image: 'assets/img/themes/Headline.png'
|
||||
}, {
|
||||
name: 'Edition',
|
||||
category: 'Newsletter',
|
||||
|
@ -108,6 +107,13 @@ const officialThemes = [{
|
|||
previewUrl: 'https://ease.ghost.io',
|
||||
ref: 'TryGhost/Ease',
|
||||
image: 'assets/img/themes/Ease.png'
|
||||
}, {
|
||||
name: 'Headline',
|
||||
category: 'News',
|
||||
url: 'https://github.com/TryGhost/Headline',
|
||||
previewUrl: 'https://headline.ghost.io',
|
||||
ref: 'TryGhost/Headline',
|
||||
image: 'assets/img/themes/Headline.png'
|
||||
}, {
|
||||
name: 'Ruby',
|
||||
category: 'Magazine',
|
||||
|
|
|
@ -18,6 +18,14 @@ export default class GhThemeTableComponent extends Component {
|
|||
this.activateTaskInstance?.cancel();
|
||||
}
|
||||
|
||||
isDefaultTheme(theme) {
|
||||
return theme.name.toLowerCase() === 'source';
|
||||
}
|
||||
|
||||
isLegacyTheme(theme) {
|
||||
return theme.name.toLowerCase() === 'casper';
|
||||
}
|
||||
|
||||
get sortedThemes() {
|
||||
let themes = this.args.themes.map((t) => {
|
||||
let theme = {};
|
||||
|
@ -30,7 +38,6 @@ export default class GhThemeTableComponent extends Component {
|
|||
theme.package = themePackage;
|
||||
theme.active = get(t, 'active');
|
||||
theme.isDeletable = !theme.active;
|
||||
|
||||
return theme;
|
||||
});
|
||||
let duplicateThemes = [];
|
||||
|
@ -44,19 +51,24 @@ export default class GhThemeTableComponent extends Component {
|
|||
});
|
||||
|
||||
duplicateThemes.forEach((theme) => {
|
||||
if (theme.name !== 'casper') {
|
||||
if (!this.isDefaultTheme(theme) && !this.isLegacyTheme(theme)) {
|
||||
theme.label = `${theme.label} (${theme.name})`;
|
||||
}
|
||||
});
|
||||
|
||||
// "(default)" needs to be added to casper manually as it's always
|
||||
// displayed and would mess up the duplicate checking if added earlier
|
||||
let casper = themes.findBy('name', 'casper');
|
||||
if (casper) {
|
||||
casper.label = `${casper.label} (default)`;
|
||||
casper.isDefault = true;
|
||||
casper.isDeletable = false;
|
||||
}
|
||||
// add (default) or (legacy) as appropriate and prevent deletion of default/legacy themes
|
||||
// this needs to be after deduplicating by label
|
||||
themes.filter(this.isDefaultTheme).forEach((theme) => {
|
||||
theme.label = `${theme.label} (default)`;
|
||||
theme.isDefault = true;
|
||||
theme.isDeletable = false;
|
||||
});
|
||||
|
||||
themes.filter(this.isLegacyTheme).forEach((theme) => {
|
||||
theme.label = `${theme.label} (legacy)`;
|
||||
theme.isLegacy = true;
|
||||
theme.isDeletable = false;
|
||||
});
|
||||
|
||||
// sorting manually because .sortBy('label') has a different sorting
|
||||
// algorithm to [...strings].sort()
|
||||
|
|
|
@ -31,8 +31,8 @@ export default class InstallThemeModal extends Component {
|
|||
return this.args.data.theme?.ref || this.args.data.ref;
|
||||
}
|
||||
|
||||
get isDefaultTheme() {
|
||||
return this.themeName.toLowerCase() === 'casper';
|
||||
get isDefaultOrLegacyTheme() {
|
||||
return this.themeName.toLowerCase() === 'casper' || this.themeName.toLowerCase() === 'source';
|
||||
}
|
||||
|
||||
get isConfirming() {
|
||||
|
@ -48,7 +48,7 @@ export default class InstallThemeModal extends Component {
|
|||
}
|
||||
|
||||
get willOverwriteExisting() {
|
||||
return !this.isDefaultTheme && this.themes.findBy('name', this.themeName.toLowerCase());
|
||||
return !this.isDefaultOrLegacyTheme && this.themes.findBy('name', this.themeName.toLowerCase());
|
||||
}
|
||||
|
||||
get hasWarningsOrErrors() {
|
||||
|
@ -67,9 +67,10 @@ export default class InstallThemeModal extends Component {
|
|||
@task
|
||||
*installThemeTask() {
|
||||
try {
|
||||
if (this.isDefaultTheme) {
|
||||
if (this.isDefaultOrLegacyTheme) {
|
||||
// default theme can't be installed, only activated
|
||||
const defaultTheme = this.store.peekRecord('theme', 'casper');
|
||||
const themeName = this.themeName.toLowerCase();
|
||||
const defaultTheme = this.store.peekRecord('theme', themeName);
|
||||
yield this.themeManagement.activateTask.perform(defaultTheme, {skipErrors: true});
|
||||
this.installedTheme = defaultTheme;
|
||||
|
||||
|
|
|
@ -91,8 +91,8 @@ export default class UploadThemeModal extends Component {
|
|||
return new UnsupportedMediaTypeError();
|
||||
}
|
||||
|
||||
if (file.name.match(/^casper\.zip$/i)) {
|
||||
return {payload: {errors: [{message: 'Sorry, the default Casper theme cannot be overwritten.<br>Please rename your zip file to continue.'}]}};
|
||||
if (file.name.match(/^casper\.zip$/i) || file.name.match(/^source\.zip$/i)) {
|
||||
return {payload: {errors: [{message: 'Sorry, the default theme cannot be overwritten.<br>Please rename your zip file to continue.'}]}};
|
||||
}
|
||||
|
||||
if (!this._allowOverwrite && this.currentThemeNames.includes(themeName)) {
|
||||
|
|
|
@ -13,18 +13,17 @@ export default class ChangeThemeController extends Controller {
|
|||
themes = this.store.peekAll('theme');
|
||||
|
||||
officialThemes = [{
|
||||
name: 'Source',
|
||||
category: 'News',
|
||||
previewUrl: 'https://source.ghost.io/',
|
||||
ref: 'default',
|
||||
image: 'assets/img/themes/Source.png'
|
||||
}, {
|
||||
name: 'Casper',
|
||||
category: 'Blog',
|
||||
previewUrl: 'https://demo.ghost.io/',
|
||||
ref: 'default',
|
||||
ref: 'TryGhost/Casper',
|
||||
image: 'assets/img/themes/Casper.png'
|
||||
}, {
|
||||
name: 'Headline',
|
||||
category: 'News',
|
||||
url: 'https://github.com/TryGhost/Headline',
|
||||
previewUrl: 'https://headline.ghost.io',
|
||||
ref: 'TryGhost/Headline',
|
||||
image: 'assets/img/themes/Headline.png'
|
||||
}, {
|
||||
name: 'Edition',
|
||||
category: 'Newsletter',
|
||||
|
@ -109,6 +108,13 @@ export default class ChangeThemeController extends Controller {
|
|||
previewUrl: 'https://ease.ghost.io',
|
||||
ref: 'TryGhost/Ease',
|
||||
image: 'assets/img/themes/Ease.png'
|
||||
}, {
|
||||
name: 'Headline',
|
||||
category: 'News',
|
||||
url: 'https://github.com/TryGhost/Headline',
|
||||
previewUrl: 'https://headline.ghost.io',
|
||||
ref: 'TryGhost/Headline',
|
||||
image: 'assets/img/themes/Headline.png'
|
||||
}, {
|
||||
name: 'Ruby',
|
||||
category: 'Magazine',
|
||||
|
|
|
@ -6,6 +6,7 @@ import {tracked} from '@glimmer/tracking';
|
|||
|
||||
const THEME_PROPERTIES = {
|
||||
casper: ['description', 'color', 'coverImage'],
|
||||
source: ['description', 'color', 'coverImage'],
|
||||
edition: ['description', 'color', 'coverImage'],
|
||||
dawn: ['description', 'color', 'icon'],
|
||||
dope: ['description', 'color', 'logo'],
|
||||
|
|
|
@ -19,7 +19,7 @@ function setting(group, key, value) {
|
|||
}
|
||||
|
||||
// These settings represent a default new site setup
|
||||
// Real default settings can be found in https://github.com/TryGhost/Ghost/blob/main/core/server/data/schema/default-settings/default-settings.json
|
||||
// Real default settings can be found in https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/data/schema/default-settings/default-settings.json
|
||||
export default [
|
||||
// SITE
|
||||
setting('site', 'title', 'Test Blog'),
|
||||
|
@ -49,7 +49,7 @@ export default [
|
|||
setting('site', 'twitter_description', null),
|
||||
|
||||
// THEME
|
||||
setting('theme', 'active_theme', 'Casper'),
|
||||
setting('theme', 'active_theme', 'Source'),
|
||||
|
||||
// PRIVATE
|
||||
setting('private', 'is_private', false),
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
export default [
|
||||
{
|
||||
name: 'source',
|
||||
package: {
|
||||
name: 'source',
|
||||
version: '1.0'
|
||||
},
|
||||
active: true
|
||||
},
|
||||
{
|
||||
name: 'casper',
|
||||
package: {
|
||||
name: 'casper',
|
||||
version: '1.0'
|
||||
},
|
||||
active: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'foo',
|
||||
|
|
BIN
ghost/admin/public/assets/img/themes/Source-Magazine.png
Normal file
BIN
ghost/admin/public/assets/img/themes/Source-Magazine.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
BIN
ghost/admin/public/assets/img/themes/Source-Newsletter.png
Normal file
BIN
ghost/admin/public/assets/img/themes/Source-Newsletter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
BIN
ghost/admin/public/assets/img/themes/Source.png
Normal file
BIN
ghost/admin/public/assets/img/themes/Source.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
|
@ -46,7 +46,7 @@ describe('Acceptance: Settings - Design', function () {
|
|||
expect(findAll('[data-test-nav-group]'), 'no of groups open').to.have.lengthOf(1);
|
||||
|
||||
// current theme is shown in nav menu
|
||||
expect(find('[data-test-text="current-theme"]')).to.contain.text('casper - v1.0');
|
||||
expect(find('[data-test-text="current-theme"]')).to.contain.text('source - v1.0');
|
||||
|
||||
// defaults to "home" desktop preview
|
||||
expect(find('[data-test-button="desktop-preview"]')).to.have.class('gh-btn-group-selected');
|
||||
|
@ -143,7 +143,7 @@ describe('Acceptance: Settings - Design', function () {
|
|||
config.hostSettings = {
|
||||
limits: {
|
||||
customThemes: {
|
||||
allowlist: ['casper', 'dawn', 'lyra'],
|
||||
allowlist: ['source', 'casper', 'dawn', 'lyra'],
|
||||
error: 'All our official built-in themes are available the Starter plan, if you upgrade to one of our higher tiers you will also be able to edit and upload custom themes for your site.'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ describe('Integration: Component: gh-theme-table', function () {
|
|||
this.set('themes', [
|
||||
{name: 'Daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
|
||||
{name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
|
||||
{name: 'source', package: {name: 'Source', version: '1.0.0'}},
|
||||
{name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
|
||||
{name: 'foo'}
|
||||
]);
|
||||
|
@ -18,14 +19,15 @@ describe('Integration: Component: gh-theme-table', function () {
|
|||
await render(hbs`<GhThemeTable @themes={{themes}} />`);
|
||||
|
||||
expect(findAll('[data-test-themes-list]').length, 'themes list is present').to.equal(1);
|
||||
expect(findAll('[data-test-theme-id]').length, 'number of rows').to.equal(4);
|
||||
expect(findAll('[data-test-theme-id]').length, 'number of rows').to.equal(5);
|
||||
|
||||
let packageNames = findAll('[data-test-theme-title]').map(name => name.textContent.trim());
|
||||
|
||||
expect(packageNames[0]).to.match(/Casper \(default\)/);
|
||||
expect(packageNames[0]).to.match(/Casper \(legacy\)/);
|
||||
expect(packageNames[1]).to.match(/Daring\s+Active/);
|
||||
expect(packageNames[2]).to.match(/foo/);
|
||||
expect(packageNames[3]).to.match(/Lanyon/);
|
||||
expect(packageNames[4]).to.match(/Source \(default\)/);
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-active="true"]').querySelector('[data-test-theme-title]'),
|
||||
|
@ -35,7 +37,7 @@ describe('Integration: Component: gh-theme-table', function () {
|
|||
expect(
|
||||
findAll('[data-test-button="activate"]').length,
|
||||
'non-active themes have an activate link'
|
||||
).to.equal(3);
|
||||
).to.equal(4);
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-active="true"]').querySelector('[data-test-button="activate"]'),
|
||||
|
@ -80,22 +82,31 @@ describe('Integration: Component: gh-theme-table', function () {
|
|||
}
|
||||
});
|
||||
|
||||
it('does not show delete action for casper', async function () {
|
||||
it('does not show delete action for default themes', async function () {
|
||||
const themes = [
|
||||
{name: 'Daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
|
||||
{name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
|
||||
{name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
|
||||
{name: 'foo'}
|
||||
{name: 'foo'},
|
||||
{name: 'source', package: {name: 'Source', version: '1.0.0'}}
|
||||
];
|
||||
this.set('themes', themes);
|
||||
|
||||
await render(hbs`<GhThemeTable @themes={{themes}} />`);
|
||||
|
||||
// Casper should not be deletable
|
||||
await click(`[data-test-theme-id="casper"] [data-test-button="actions"]`);
|
||||
expect(find('[data-test-actions-for="casper"]')).to.exist;
|
||||
expect(
|
||||
find(`[data-test-actions-for="casper"] [data-test-button="delete"]`)
|
||||
).to.not.exist;
|
||||
|
||||
// Source should not be deletable
|
||||
await click(`[data-test-theme-id="source"] [data-test-button="actions"]`);
|
||||
expect(find('[data-test-actions-for="source"]')).to.exist;
|
||||
expect(
|
||||
find(`[data-test-actions-for="source"] [data-test-button="delete"]`)
|
||||
).to.not.exist;
|
||||
});
|
||||
|
||||
it('does not show delete action for active theme', async function () {
|
||||
|
@ -120,6 +131,7 @@ describe('Integration: Component: gh-theme-table', function () {
|
|||
this.set('themes', [
|
||||
{name: 'daring', package: {name: 'Daring', version: '0.1.4'}},
|
||||
{name: 'daring-0.1.5', package: {name: 'Daring', version: '0.1.4'}},
|
||||
{name: 'source', package: {name: 'Source', version: '1.0.0'}},
|
||||
{name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
|
||||
{name: 'another', package: {name: 'Casper', version: '1.3.1'}},
|
||||
{name: 'mine', package: {name: 'Casper', version: '1.3.1'}},
|
||||
|
@ -135,11 +147,12 @@ describe('Integration: Component: gh-theme-table', function () {
|
|||
'themes are ordered by label, folder names shown for duplicates'
|
||||
).to.deep.equal([
|
||||
'Casper (another)',
|
||||
'Casper (default)',
|
||||
'Casper (legacy)',
|
||||
'Casper (mine)',
|
||||
'Daring (daring)',
|
||||
'Daring (daring-0.1.5)',
|
||||
'foo'
|
||||
'foo',
|
||||
'Source (default)'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,6 +32,8 @@ content/settings/**
|
|||
content/themes/**
|
||||
!content/themes/casper/**
|
||||
content/themes/casper/yarn.lock
|
||||
!content/themes/source/**
|
||||
content/themes/source/yarn.lock
|
||||
node_modules/**
|
||||
core/server/lib/members/static/auth/node_modules/**
|
||||
**/*.db
|
||||
|
@ -50,6 +52,7 @@ core/built/**/tests-*
|
|||
test/**
|
||||
CONTRIBUTING.md
|
||||
content/themes/casper/SECURITY.md
|
||||
content/themes/source/SECURITY.md
|
||||
SECURITY.md
|
||||
renovate.json
|
||||
*.html
|
||||
|
@ -61,7 +64,9 @@ bower_components/**
|
|||
.editorconfig
|
||||
gulpfile.js
|
||||
!content/themes/casper/gulpfile.js
|
||||
!content/themes/source/gulpfile.js
|
||||
package-lock.json
|
||||
content/themes/casper/config.*.json
|
||||
content/themes/source/config.*.json
|
||||
config.*.json
|
||||
!core/shared/config/env/**
|
||||
|
|
1
ghost/core/content/themes/source
Submodule
1
ghost/core/content/themes/source
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit e0483fd7f4d6a9e2d5c26c1e89d4322f1f2702d6
|
|
@ -0,0 +1,40 @@
|
|||
// For information on writing migrations, see https://www.notion.so/ghost/Database-migrations-eb5b78c435d741d2b34a582d57c24253
|
||||
|
||||
const logging = require('@tryghost/logging');
|
||||
const {createTransactionalMigration} = require('../../utils');
|
||||
|
||||
// For DDL - schema changes
|
||||
// const {createNonTransactionalMigration} = require('../../utils');
|
||||
|
||||
// For DML - data changes
|
||||
// const {createTransactionalMigration} = require('../../utils');
|
||||
|
||||
// Or use a specific helper
|
||||
// const {addTable, createAddColumnMigration} = require('../../utils');
|
||||
|
||||
module.exports = createTransactionalMigration(
|
||||
async function up() {
|
||||
// don't do anything
|
||||
// we don't want to change the active theme automatically
|
||||
},
|
||||
async function down(knex) {
|
||||
// If the active theme is `source`, we want to revert the active theme to `casper`
|
||||
// `source` is introduced in 5.67, so it does not exist in < 5.67
|
||||
// Without this change, rolling back from 5.67 to < 5.67 results in an error:
|
||||
// The currently active theme "source" is missing.
|
||||
const rows = await knex
|
||||
.select('value')
|
||||
.from('settings')
|
||||
.where({key: 'active_theme', value: 'source'});
|
||||
|
||||
if (rows.length === 0) {
|
||||
logging.info(`Currently installed theme is not source - skipping migration`);
|
||||
return;
|
||||
}
|
||||
|
||||
logging.info(`Resetting the active theme to casper`);
|
||||
await knex('settings')
|
||||
.where('key', 'active_theme')
|
||||
.update({value: 'casper'});
|
||||
}
|
||||
);
|
|
@ -216,7 +216,7 @@
|
|||
},
|
||||
"theme": {
|
||||
"active_theme": {
|
||||
"defaultValue": "casper",
|
||||
"defaultValue": "source",
|
||||
"flags": "RO",
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
@ -200,8 +200,8 @@ async function installTheme(data, api) {
|
|||
return data;
|
||||
}
|
||||
|
||||
if (themeName.toLowerCase() === 'tryghost/casper') {
|
||||
logging.warn('Skipping theme install as Casper is the default theme.');
|
||||
if (themeName.toLowerCase() === 'tryghost/source') {
|
||||
logging.warn('Skipping theme install as Source is the default theme.');
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ async function installTheme(data, api) {
|
|||
context: {internal: true}
|
||||
});
|
||||
} catch (error) {
|
||||
//Fallback to Casper by doing nothing as the theme setting update is the last step
|
||||
//Fallback to Casper/Source by doing nothing as the theme setting update is the last step
|
||||
logging.warn(tpl(messages.failedThemeInstall, {themeName, error: error.message}));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ const settingsCache = require('../../../shared/settings-cache');
|
|||
const messages = {
|
||||
themeDoesNotExist: 'Theme does not exist.',
|
||||
invalidThemeName: 'Please select a valid theme.',
|
||||
overrideCasper: 'Please rename your zip, it\'s not allowed to override the default casper theme.',
|
||||
destroyCasper: 'Deleting the default casper theme is not allowed.',
|
||||
overrideDefaultTheme: 'Please rename your zip, it\'s not allowed to override the default theme.',
|
||||
destroyDefaultTheme: 'Deleting the default theme is not allowed.',
|
||||
destroyActive: 'Deleting the active theme is not allowed.'
|
||||
};
|
||||
|
||||
|
@ -49,10 +49,10 @@ module.exports = {
|
|||
const themeName = getStorage().getSanitizedFileName(zip.name.split('.zip')[0]);
|
||||
const backupName = `${themeName}_${ObjectID()}`;
|
||||
|
||||
// check if zip name is casper.zip
|
||||
if (zip.name === 'casper.zip') {
|
||||
// check if zip name matches one of the default themes
|
||||
if (zip.name === 'casper.zip' || zip.name === 'source.zip') {
|
||||
throw new errors.ValidationError({
|
||||
message: tpl(messages.overrideCasper)
|
||||
message: tpl(messages.overrideDefaultTheme)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -127,9 +127,9 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
destroy: async function (themeName) {
|
||||
if (themeName === 'casper') {
|
||||
if (themeName === 'casper' || themeName === 'source') {
|
||||
throw new errors.ValidationError({
|
||||
message: tpl(messages.destroyCasper)
|
||||
message: tpl(messages.destroyDefaultTheme)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
test/coverage/**
|
||||
test/utils/fixtures/themes/casper/assets/**
|
||||
test/utils/fixtures/themes/source/assets/**
|
||||
|
|
|
@ -98,7 +98,7 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"key": "active_theme",
|
||||
"value": "casper",
|
||||
"value": "source",
|
||||
},
|
||||
Object {
|
||||
"key": "is_private",
|
||||
|
@ -367,7 +367,7 @@ Object {
|
|||
"settings": Array [
|
||||
Object {
|
||||
"key": "active_theme",
|
||||
"value": "casper",
|
||||
"value": "source",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -508,7 +508,7 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"key": "active_theme",
|
||||
"value": "casper",
|
||||
"value": "source",
|
||||
},
|
||||
Object {
|
||||
"key": "is_private",
|
||||
|
@ -866,7 +866,7 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"key": "active_theme",
|
||||
"value": "casper",
|
||||
"value": "source",
|
||||
},
|
||||
Object {
|
||||
"key": "is_private",
|
||||
|
@ -1223,7 +1223,7 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"key": "active_theme",
|
||||
"value": "casper",
|
||||
"value": "source",
|
||||
},
|
||||
Object {
|
||||
"key": "is_private",
|
||||
|
@ -1585,7 +1585,7 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"key": "active_theme",
|
||||
"value": "casper",
|
||||
"value": "source",
|
||||
},
|
||||
Object {
|
||||
"key": "is_private",
|
||||
|
@ -2035,7 +2035,7 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"key": "active_theme",
|
||||
"value": "casper",
|
||||
"value": "source",
|
||||
},
|
||||
Object {
|
||||
"key": "is_private",
|
||||
|
@ -2457,7 +2457,7 @@ Object {
|
|||
},
|
||||
Object {
|
||||
"key": "active_theme",
|
||||
"value": "casper",
|
||||
"value": "source",
|
||||
},
|
||||
Object {
|
||||
"key": "is_private",
|
||||
|
|
|
@ -45,17 +45,17 @@ describe('Themes API', function () {
|
|||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse.themes);
|
||||
localUtils.API.checkResponse(jsonResponse, 'themes');
|
||||
jsonResponse.themes.length.should.eql(6);
|
||||
jsonResponse.themes.length.should.eql(7);
|
||||
|
||||
localUtils.API.checkResponse(jsonResponse.themes[0], 'theme');
|
||||
jsonResponse.themes[0].name.should.eql('broken-theme');
|
||||
jsonResponse.themes[0].package.should.be.an.Object().with.properties('name', 'version');
|
||||
jsonResponse.themes[0].active.should.be.false();
|
||||
|
||||
localUtils.API.checkResponse(jsonResponse.themes[1], 'theme', 'templates');
|
||||
localUtils.API.checkResponse(jsonResponse.themes[1], 'theme');
|
||||
jsonResponse.themes[1].name.should.eql('casper');
|
||||
jsonResponse.themes[1].package.should.be.an.Object().with.properties('name', 'version');
|
||||
jsonResponse.themes[1].active.should.be.true();
|
||||
jsonResponse.themes[1].active.should.be.false();
|
||||
|
||||
localUtils.API.checkResponse(jsonResponse.themes[2], 'theme');
|
||||
jsonResponse.themes[2].name.should.eql('locale-theme');
|
||||
|
@ -67,15 +67,20 @@ describe('Themes API', function () {
|
|||
jsonResponse.themes[3].package.should.be.an.Object().with.properties('name', 'version');
|
||||
jsonResponse.themes[3].active.should.be.false();
|
||||
|
||||
localUtils.API.checkResponse(jsonResponse.themes[4], 'theme');
|
||||
jsonResponse.themes[4].name.should.eql('test-theme');
|
||||
localUtils.API.checkResponse(jsonResponse.themes[4], 'theme', 'templates');
|
||||
jsonResponse.themes[4].name.should.eql('source');
|
||||
jsonResponse.themes[4].package.should.be.an.Object().with.properties('name', 'version');
|
||||
jsonResponse.themes[4].active.should.be.false();
|
||||
jsonResponse.themes[4].active.should.be.true();
|
||||
|
||||
localUtils.API.checkResponse(jsonResponse.themes[5], 'theme');
|
||||
jsonResponse.themes[5].name.should.eql('test-theme-channels');
|
||||
jsonResponse.themes[5].package.should.be.false();
|
||||
jsonResponse.themes[5].name.should.eql('test-theme');
|
||||
jsonResponse.themes[5].package.should.be.an.Object().with.properties('name', 'version');
|
||||
jsonResponse.themes[5].active.should.be.false();
|
||||
|
||||
localUtils.API.checkResponse(jsonResponse.themes[6], 'theme');
|
||||
jsonResponse.themes[6].name.should.eql('test-theme-channels');
|
||||
jsonResponse.themes[6].package.should.be.false();
|
||||
jsonResponse.themes[6].active.should.be.false();
|
||||
});
|
||||
|
||||
it('Can download a theme', async function () {
|
||||
|
@ -129,13 +134,13 @@ describe('Themes API', function () {
|
|||
|
||||
should.exist(jsonResponse3.themes);
|
||||
localUtils.API.checkResponse(jsonResponse3, 'themes');
|
||||
jsonResponse3.themes.length.should.eql(7);
|
||||
jsonResponse3.themes.length.should.eql(8);
|
||||
|
||||
// Casper should be present and still active
|
||||
const casperTheme = _.find(jsonResponse3.themes, {name: 'casper'});
|
||||
should.exist(casperTheme);
|
||||
localUtils.API.checkResponse(casperTheme, 'theme', 'templates');
|
||||
casperTheme.active.should.be.true();
|
||||
// Source should be present and still active
|
||||
const sourceTheme = _.find(jsonResponse3.themes, {name: 'source'});
|
||||
should.exist(sourceTheme);
|
||||
localUtils.API.checkResponse(sourceTheme, 'theme', 'templates');
|
||||
sourceTheme.active.should.be.true();
|
||||
|
||||
// The added theme should be here
|
||||
const addedTheme = _.find(jsonResponse3.themes, {name: 'valid'});
|
||||
|
@ -149,6 +154,7 @@ describe('Themes API', function () {
|
|||
'casper',
|
||||
'locale-theme',
|
||||
'members-test-theme',
|
||||
'source',
|
||||
'test-theme',
|
||||
'test-theme-channels',
|
||||
'valid'
|
||||
|
@ -171,7 +177,7 @@ describe('Themes API', function () {
|
|||
tmpFolderContents.splice(i, 1);
|
||||
}
|
||||
}
|
||||
tmpFolderContents.should.be.an.Array().with.lengthOf(10);
|
||||
tmpFolderContents.should.be.an.Array().with.lengthOf(11);
|
||||
|
||||
tmpFolderContents.should.eql([
|
||||
'broken-theme',
|
||||
|
@ -180,6 +186,7 @@ describe('Themes API', function () {
|
|||
'invalid.zip',
|
||||
'locale-theme',
|
||||
'members-test-theme',
|
||||
'source',
|
||||
'test-theme',
|
||||
'test-theme-channels',
|
||||
'valid.zip',
|
||||
|
@ -196,13 +203,13 @@ describe('Themes API', function () {
|
|||
|
||||
should.exist(jsonResponse2.themes);
|
||||
localUtils.API.checkResponse(jsonResponse2, 'themes');
|
||||
jsonResponse2.themes.length.should.eql(6);
|
||||
jsonResponse2.themes.length.should.eql(7);
|
||||
|
||||
// Casper should be present and still active
|
||||
const casperTheme = _.find(jsonResponse2.themes, {name: 'casper'});
|
||||
should.exist(casperTheme);
|
||||
localUtils.API.checkResponse(casperTheme, 'theme', 'templates');
|
||||
casperTheme.active.should.be.true();
|
||||
// Source should be present and still active
|
||||
const sourceTheme = _.find(jsonResponse2.themes, {name: 'source'});
|
||||
should.exist(sourceTheme);
|
||||
localUtils.API.checkResponse(sourceTheme, 'theme', 'templates');
|
||||
sourceTheme.active.should.be.true();
|
||||
|
||||
// The deleted theme should not be here
|
||||
const deletedTheme = _.find(jsonResponse2.themes, {name: 'valid'});
|
||||
|
@ -238,12 +245,12 @@ describe('Themes API', function () {
|
|||
|
||||
should.exist(jsonResponse.themes);
|
||||
localUtils.API.checkResponse(jsonResponse, 'themes');
|
||||
jsonResponse.themes.length.should.eql(6);
|
||||
jsonResponse.themes.length.should.eql(7);
|
||||
|
||||
const casperTheme = _.find(jsonResponse.themes, {name: 'casper'});
|
||||
should.exist(casperTheme);
|
||||
localUtils.API.checkResponse(casperTheme, 'theme', 'templates');
|
||||
casperTheme.active.should.be.true();
|
||||
const sourceTheme = _.find(jsonResponse.themes, {name: 'source'});
|
||||
should.exist(sourceTheme);
|
||||
localUtils.API.checkResponse(sourceTheme, 'theme', 'templates');
|
||||
sourceTheme.active.should.be.true();
|
||||
|
||||
const testTheme = _.find(jsonResponse.themes, {name: 'test-theme'});
|
||||
should.exist(testTheme);
|
||||
|
@ -263,8 +270,8 @@ describe('Themes API', function () {
|
|||
localUtils.API.checkResponse(jsonResponse2, 'themes');
|
||||
jsonResponse2.themes.length.should.eql(1);
|
||||
|
||||
const casperTheme2 = _.find(jsonResponse2.themes, {name: 'casper'});
|
||||
should.not.exist(casperTheme2);
|
||||
const sourceTheme2 = _.find(jsonResponse2.themes, {name: 'source'});
|
||||
should.not.exist(sourceTheme2);
|
||||
|
||||
const testTheme2 = _.find(jsonResponse2.themes, {name: 'test-theme'});
|
||||
should.exist(testTheme2);
|
||||
|
@ -318,7 +325,7 @@ describe('Themes API', function () {
|
|||
});
|
||||
|
||||
it('Can re-upload the active theme to override', async function () {
|
||||
// The tricky thing about this test is the default active theme is Casper and you're not allowed to override it.
|
||||
// The tricky thing about this test is the default active theme is Source and you're not allowed to override it.
|
||||
// So we upload a valid theme, activate it, and then upload again.
|
||||
sinon.stub(settingsCache, 'get').callsFake(function (key, options) {
|
||||
if (key === 'active_theme') {
|
||||
|
|
|
@ -68,7 +68,6 @@ describe('Default Frontend routing', function () {
|
|||
|
||||
$('body.home-template').length.should.equal(1);
|
||||
$('article.post').length.should.equal(7);
|
||||
$('article.tag-getting-started').length.should.equal(7);
|
||||
|
||||
res.text.should.not.containEql('__GHOST_URL__');
|
||||
});
|
||||
|
|
|
@ -114,7 +114,7 @@ describe('Importer', function () {
|
|||
return models.Settings.findOne(_.merge({key: 'active_theme'}, testUtils.context.internal));
|
||||
})
|
||||
.then(function (result) {
|
||||
result.attributes.value.should.eql('casper');
|
||||
result.attributes.value.should.eql('source');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ describe('Authentication API', function () {
|
|||
const password = 'thisissupersafe';
|
||||
|
||||
const requestMock = nock('https://api.github.com')
|
||||
.get('/repos/tryghost/casper/zipball')
|
||||
.get('/repos/tryghost/source/zipball')
|
||||
.query(true)
|
||||
.replyWithFile(200, fixtureManager.getPathForFixture('themes/valid.zip'));
|
||||
|
||||
|
@ -207,7 +207,7 @@ describe('Authentication API', function () {
|
|||
email,
|
||||
password,
|
||||
blogTitle: 'a test blog',
|
||||
theme: 'TryGhost/Casper',
|
||||
theme: 'TryGhost/Source',
|
||||
accentColor: '#85FF00',
|
||||
description: 'Custom Site Description on Setup — great for everyone'
|
||||
}]
|
||||
|
@ -236,7 +236,7 @@ describe('Authentication API', function () {
|
|||
const activeTheme = await settingsCache.get('active_theme');
|
||||
const accentColor = await settingsCache.get('accent_color');
|
||||
const description = await settingsCache.get('description');
|
||||
assert.equal(activeTheme, 'casper', 'The theme casper should have been installed');
|
||||
assert.equal(activeTheme, 'source', 'The theme Source should have been installed');
|
||||
assert.equal(accentColor, '#85FF00', 'The accent color should have been set');
|
||||
assert.equal(description, 'Custom Site Description on Setup — great for everyone', 'The site description should have been set');
|
||||
|
||||
|
|
|
@ -65,8 +65,7 @@ describe('Dynamic Routing', function () {
|
|||
|
||||
$('title').text().should.equal('Ghost');
|
||||
$('body.home-template').length.should.equal(1);
|
||||
$('article.post').length.should.equal(5);
|
||||
$('article.tag-getting-started').length.should.equal(5);
|
||||
$('article.post').length.should.equal(7);
|
||||
|
||||
done();
|
||||
});
|
||||
|
@ -142,9 +141,8 @@ describe('Dynamic Routing', function () {
|
|||
should.not.exist(res.headers['set-cookie']);
|
||||
should.exist(res.headers.date);
|
||||
|
||||
$('body').attr('class').should.eql('tag-template tag-getting-started');
|
||||
$('body').attr('class').should.eql('tag-template tag-getting-started has-sans-title has-sans-body');
|
||||
$('article.post').length.should.equal(5);
|
||||
$('article.tag-getting-started').length.should.equal(5);
|
||||
|
||||
done();
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ describe('DB version integrity', function () {
|
|||
// Only these variables should need updating
|
||||
const currentSchemaHash = '1b75aae9befefea53b17c9c1991c8a1d';
|
||||
const currentFixturesHash = '4db87173699ad9c9d8a67ccab96dfd2d';
|
||||
const currentSettingsHash = '3a7ca0aa6a06cba47e3e898aef7029c2';
|
||||
const currentSettingsHash = '3128d4ec667a50049486b0c21f04be07';
|
||||
const currentRoutesHash = '3d180d52c663d173a6be791ef411ed01';
|
||||
|
||||
// If this test is failing, then it is likely a change has been made that requires a DB version bump,
|
||||
|
|
|
@ -124,7 +124,7 @@ const prepareContentFolder = async ({contentFolder, redirectsFile = true, routes
|
|||
await fs.ensureDir(path.join(contentFolderForTests, 'adapters'));
|
||||
await fs.ensureDir(path.join(contentFolderForTests, 'settings'));
|
||||
|
||||
// Copy all themes into the new test content folder. Default active theme is always casper.
|
||||
// Copy all themes into the new test content folder. Default active theme is always source.
|
||||
// If you want to use a different theme, you have to set the active theme (e.g. stub)
|
||||
await fs.copy(
|
||||
path.join(__dirname, 'fixtures', 'themes'),
|
||||
|
|
|
@ -75,12 +75,12 @@ const prepareContentFolder = (options) => {
|
|||
fs.ensureDirSync(path.join(contentFolderForTests, 'settings'));
|
||||
|
||||
if (options.copyThemes) {
|
||||
// Copy all themes into the new test content folder. Default active theme is always casper. If you want to use a different theme, you have to set the active theme (e.g. stub)
|
||||
// Copy all themes into the new test content folder. Default active theme is always source. If you want to use a different theme, you have to set the active theme (e.g. stub)
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'themes'), path.join(contentFolderForTests, 'themes'));
|
||||
}
|
||||
|
||||
// Copy theme even if frontend is disabled, as admin can use casper when viewing themes section
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'themes', 'casper'), path.join(contentFolderForTests, 'themes', 'casper'));
|
||||
// Copy theme even if frontend is disabled, as admin can use source when viewing themes section
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'themes', 'source'), path.join(contentFolderForTests, 'themes', 'source'));
|
||||
|
||||
if (options.redirectsFile) {
|
||||
redirects.setupFile(contentFolderForTests, options.redirectsFileExt);
|
||||
|
|
|
@ -212,7 +212,7 @@
|
|||
},
|
||||
"theme": {
|
||||
"active_theme": {
|
||||
"defaultValue": "casper",
|
||||
"defaultValue": "source",
|
||||
"flags": "RO",
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
@ -224,7 +224,7 @@
|
|||
},
|
||||
"theme": {
|
||||
"active_theme": {
|
||||
"defaultValue": "casper",
|
||||