mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
API Upgrade to behat 3
This commit is contained in:
parent
71fd734440
commit
df100b8798
@ -52,7 +52,7 @@ before_script:
|
|||||||
|
|
||||||
script:
|
script:
|
||||||
- "if [ \"$PHPUNIT_TEST\" = \"1\" ]; then vendor/bin/phpunit tests flush=1; fi"
|
- "if [ \"$PHPUNIT_TEST\" = \"1\" ]; then vendor/bin/phpunit tests flush=1; fi"
|
||||||
- "if [ \"$BEHAT_TEST\" = \"1\" ]; then vendor/bin/behat .; fi"
|
- "if [ \"$BEHAT_TEST\" = \"1\" ]; then vendor/bin/behat @cms; fi"
|
||||||
- "if [ \"$NPM_TEST\" = \"1\" ]; then git diff-files --quiet -w --relative=client; fi"
|
- "if [ \"$NPM_TEST\" = \"1\" ]; then git diff-files --quiet -w --relative=client; fi"
|
||||||
- "if [ \"$NPM_TEST\" = \"1\" ]; then git diff -w --no-color --relative=client; fi"
|
- "if [ \"$NPM_TEST\" = \"1\" ]; then git diff -w --no-color --relative=client; fi"
|
||||||
- "if [ \"$NPM_TEST\" = \"1\" ]; then (nvm use $TRAVIS_NODE_VERSION && npm run lint); fi"
|
- "if [ \"$NPM_TEST\" = \"1\" ]; then (nvm use $TRAVIS_NODE_VERSION && npm run lint); fi"
|
||||||
|
50
behat.yml
50
behat.yml
@ -1,37 +1,27 @@
|
|||||||
default:
|
default:
|
||||||
context:
|
suites:
|
||||||
class: SilverStripe\Cms\Test\Behaviour\FeatureContext
|
cms:
|
||||||
|
paths:
|
||||||
|
- %paths.modules.cms%/tests/behat/features
|
||||||
|
contexts:
|
||||||
|
- SilverStripe\Framework\Tests\Behaviour\FeatureContext
|
||||||
|
- SilverStripe\Framework\Tests\Behaviour\CmsFormsContext
|
||||||
|
- SilverStripe\Framework\Tests\Behaviour\CmsUiContext
|
||||||
|
- SilverStripe\BehatExtension\Context\BasicContext
|
||||||
|
- SilverStripe\BehatExtension\Context\EmailContext
|
||||||
|
- SilverStripe\CMS\Tests\Behaviour\LoginContext
|
||||||
|
- SilverStripe\CMS\Tests\Behaviour\ThemeContext
|
||||||
|
- SilverStripe\CMS\Tests\Behaviour\FixtureContext:
|
||||||
|
# Note: double indent for args is intentional
|
||||||
|
- %paths.modules.cms%/tests/behat/features/files/
|
||||||
|
|
||||||
extensions:
|
extensions:
|
||||||
SilverStripe\BehatExtension\Extension:
|
SilverStripe\BehatExtension\MinkExtension:
|
||||||
framework_path: framework
|
|
||||||
bootstrap_file: "tests/behat/serve-bootstrap.php"
|
|
||||||
Behat\MinkExtension\Extension:
|
|
||||||
# Designed to run using the below
|
|
||||||
# `vendor/bin/serve --bootstrap-file tests/behat/serve-bootstrap.php`
|
|
||||||
# `vendor/bin/selenium-server-standalone -Dwebdriver.firefox.bin="/Applications/Firefox31.app/Contents/MacOS/firefox-bin"`
|
|
||||||
base_url: http://localhost:8080/
|
|
||||||
files_path: %behat.paths.base%/framework/tests/behat/features/files/
|
|
||||||
default_session: selenium2
|
default_session: selenium2
|
||||||
javascript_session: selenium2
|
javascript_session: selenium2
|
||||||
selenium2:
|
selenium2:
|
||||||
browser: firefox
|
browser: firefox
|
||||||
|
|
||||||
# Alternative profile, use with "vendor/bin/behat --profile chrome"
|
SilverStripe\BehatExtension\Extension:
|
||||||
chrome:
|
screenshot_path: %behat.paths.base%/tests/behat/artifacts/screenshots
|
||||||
extensions:
|
bootstrap_file: "tests/behat/serve-bootstrap.php"
|
||||||
Behat\MinkExtension\Extension:
|
|
||||||
selenium2:
|
|
||||||
browser: chrome
|
|
||||||
|
|
||||||
# Saucelabs.com sample setup, use with "vendor/bin/behat --profile saucelabs"
|
|
||||||
saucelabs:
|
|
||||||
extensions:
|
|
||||||
Behat\MinkExtension\Extension:
|
|
||||||
selenium2:
|
|
||||||
browser: firefox
|
|
||||||
# Add your own username and API token here
|
|
||||||
wd_host: <user>:<api-token>@ondemand.saucelabs.com/wd/hub
|
|
||||||
capabilities:
|
|
||||||
platform: "Windows 2008"
|
|
||||||
browser: "firefox"
|
|
||||||
version: "15"
|
|
||||||
|
@ -26,9 +26,9 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^5.7",
|
"phpunit/phpunit": "^5.7",
|
||||||
"silverstripe/behat-extension": "^2.1.0",
|
"silverstripe/behat-extension": "^3",
|
||||||
"silverstripe/serve": "dev-master",
|
"silverstripe/serve": "dev-master",
|
||||||
"silverstripe/testsession": "^2.0.0-alpha3",
|
"silverstripe/testsession": "^3",
|
||||||
"se/selenium-server-standalone": "2.41.0"
|
"se/selenium-server-standalone": "2.41.0"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
@ -41,9 +41,9 @@
|
|||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"SilverStripe\\CMS\\": "code/"
|
"SilverStripe\\CMS\\": "code/",
|
||||||
},
|
"SilverStripe\\CMS\\Tests\\Behaviour\\": "tests/behat/src/"
|
||||||
"classmap": ["tests/behat/"]
|
}
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev"
|
"minimum-stability": "dev"
|
||||||
}
|
}
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\Cms\Test\Behaviour;
|
|
||||||
|
|
||||||
use SilverStripe\BehatExtension\Context\SilverStripeContext;
|
|
||||||
use SilverStripe\BehatExtension\Context\BasicContext;
|
|
||||||
use SilverStripe\BehatExtension\Context\LoginContext;
|
|
||||||
use SilverStripe\BehatExtension\Context\FixtureContext;
|
|
||||||
use SilverStripe\Framework\Test\Behaviour\CmsFormsContext;
|
|
||||||
use SilverStripe\Framework\Test\Behaviour\CmsUiContext;
|
|
||||||
use SilverStripe\Cms\Test\Behaviour;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\Core\ClassInfo;
|
|
||||||
use SilverStripe\Core\Injector\Injector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Features context
|
|
||||||
*
|
|
||||||
* Context automatically loaded by Behat.
|
|
||||||
* Uses subcontexts to extend functionality.
|
|
||||||
*/
|
|
||||||
class FeatureContext extends \SilverStripe\Framework\Test\Behaviour\FeatureContext
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes context.
|
|
||||||
* Every scenario gets it's own context object.
|
|
||||||
*
|
|
||||||
* @param array $parameters context parameters (set them up through behat.yml)
|
|
||||||
*/
|
|
||||||
public function __construct(array $parameters)
|
|
||||||
{
|
|
||||||
parent::__construct($parameters);
|
|
||||||
|
|
||||||
// Override existing fixture context with more specific one
|
|
||||||
$fixtureContext = new \SilverStripe\Cms\Test\Behaviour\FixtureContext($parameters);
|
|
||||||
$fixtureContext->setFixtureFactory($this->getFixtureFactory());
|
|
||||||
$this->useContext('FixtureContext', $fixtureContext);
|
|
||||||
|
|
||||||
// Add extra contexts with more steps
|
|
||||||
$this->useContext('ThemeContext', new \SilverStripe\Cms\Test\Behaviour\ThemeContext($parameters));
|
|
||||||
|
|
||||||
// Use blueprints which auto-publish all subclasses of SiteTree
|
|
||||||
$factory = $fixtureContext->getFixtureFactory();
|
|
||||||
foreach (ClassInfo::subclassesFor('SilverStripe\\CMS\\Model\\SiteTree') as $id => $class) {
|
|
||||||
$blueprint = Injector::inst()->create('SilverStripe\\Dev\\FixtureBlueprint', $class);
|
|
||||||
$blueprint->addCallback('afterCreate', function ($obj, $identifier, &$data, &$fixtures) {
|
|
||||||
/** @var SiteTree $obj */
|
|
||||||
$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
|
||||||
});
|
|
||||||
$factory->define($class, $blueprint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\Cms\Test\Behaviour;
|
|
||||||
|
|
||||||
use Behat\Behat\Context\ClosuredContextInterface;
|
|
||||||
use Behat\Behat\Context\TranslatedContextInterface;
|
|
||||||
use Behat\Behat\Context\BehatContext;
|
|
||||||
use Behat\Behat\Context\Step;
|
|
||||||
use Behat\Behat\Event\StepEvent;
|
|
||||||
use Behat\Behat\Exception\PendingException;
|
|
||||||
use Behat\Mink\Driver\Selenium2Driver;
|
|
||||||
use Behat\Gherkin\Node\PyStringNode;
|
|
||||||
use Behat\Gherkin\Node\TableNode;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context used to create fixtures in the SilverStripe ORM.
|
|
||||||
*/
|
|
||||||
class FixtureContext extends \SilverStripe\BehatExtension\Context\FixtureContext
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find or create a redirector page and link to another existing page.
|
|
||||||
* Example: Given a "page" "My Redirect" which redirects to a "page" "Page 1"
|
|
||||||
*
|
|
||||||
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" (:?which )?redirects to (?:(an|a|the) )"(?<targetType>[^"]+)" "(?<targetId>[^"]+)"$/
|
|
||||||
*/
|
|
||||||
public function stepCreateRedirectorPage($type, $id, $targetType, $targetId)
|
|
||||||
{
|
|
||||||
$class = 'SilverStripe\\CMS\\Model\\RedirectorPage';
|
|
||||||
$targetClass = $this->convertTypeToClass($targetType);
|
|
||||||
|
|
||||||
$targetObj = $this->fixtureFactory->get($targetClass, $targetId);
|
|
||||||
if (!$targetObj) {
|
|
||||||
$targetObj = $this->fixtureFactory->get($targetClass, $targetId);
|
|
||||||
}
|
|
||||||
|
|
||||||
$fields = array('LinkToID' => $targetObj->ID);
|
|
||||||
/** @var \RedirectorPage $obj */
|
|
||||||
$obj = $this->fixtureFactory->get($class, $id);
|
|
||||||
if ($obj) {
|
|
||||||
$obj->update($fields);
|
|
||||||
} else {
|
|
||||||
$obj = $this->fixtureFactory->createObject($class, $id, $fields);
|
|
||||||
}
|
|
||||||
$obj->write();
|
|
||||||
$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Check if the user can edit a page
|
|
||||||
*
|
|
||||||
* Example: Then pages should be editable by "Admin"
|
|
||||||
* Then pages should not be editable by "Admin"
|
|
||||||
*
|
|
||||||
* @Then /^pages should( not? |\s*)be editable by "([^"]*)"$/
|
|
||||||
*/
|
|
||||||
public function pagesShouldBeEditableBy($negative, $member)
|
|
||||||
{
|
|
||||||
$page = \Page::get()->First();
|
|
||||||
|
|
||||||
return array(
|
|
||||||
new Step\Given('I am not logged in'),
|
|
||||||
new Step\Given('I am logged in with "' . $member . '" permissions'),
|
|
||||||
new Step\Given('I go to "/admin/pages/edit/show/' . $page->ID . '"'),
|
|
||||||
new Step\Given('I should' . $negative . 'see a "Page name" field'),
|
|
||||||
new Step\Then('I am on the homepage')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,7 @@ Feature: Duplicate a page
|
|||||||
And the "page" "Page1b" is a child of the "page" "Page1"
|
And the "page" "Page1b" is a child of the "page" "Page1"
|
||||||
And the "page" "Page1b1" is a child of the "page" "Page1b"
|
And the "page" "Page1b1" is a child of the "page" "Page1b"
|
||||||
|
|
||||||
@javascript
|
@javascript @retry
|
||||||
Scenario: I can duplicate a page in the pages section
|
Scenario: I can duplicate a page in the pages section
|
||||||
When I go to "/admin/pages"
|
When I go to "/admin/pages"
|
||||||
And I right click on "Page1" in the tree
|
And I right click on "Page1" in the tree
|
||||||
@ -20,6 +20,5 @@ Feature: Duplicate a page
|
|||||||
|
|
||||||
When I fill in "Title" with "Duplicate Page"
|
When I fill in "Title" with "Duplicate Page"
|
||||||
And I press the "Save & publish" button
|
And I press the "Save & publish" button
|
||||||
And I wait for 1 second
|
|
||||||
Then I should see "Page1" in the tree
|
Then I should see "Page1" in the tree
|
||||||
And I should see "Duplicate Page" in the tree
|
And I should see "Duplicate Page" in the tree
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@assets
|
@assets @retry
|
||||||
Feature: Insert links into a page
|
Feature: Insert links into a page
|
||||||
As a cms author
|
As a cms author
|
||||||
I want to insert a link into my content
|
I want to insert a link into my content
|
||||||
@ -30,7 +30,6 @@ So that I can link to a external website or a page on my site
|
|||||||
And I press the "Insert Link" HTML field button
|
And I press the "Insert Link" HTML field button
|
||||||
And I select the "Link to a page on this site" radio button
|
And I select the "Link to a page on this site" radio button
|
||||||
And I fill in the "internal" dropdown with "Details"
|
And I fill in the "internal" dropdown with "Details"
|
||||||
And I wait for 1 second
|
|
||||||
And I select "youranchor" from "Form_EditorToolbarLinkForm_AnchorSelector"
|
And I select "youranchor" from "Form_EditorToolbarLinkForm_AnchorSelector"
|
||||||
And I press the "Insert" button
|
And I press the "Insert" button
|
||||||
Then the "Content" HTML field should contain "<a href="[sitetree_link,id=3]#youranchor">awesome</a>"
|
Then the "Content" HTML field should contain "<a href="[sitetree_link,id=3]#youranchor">awesome</a>"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
@retry
|
||||||
Feature: Search for a page
|
Feature: Search for a page
|
||||||
As an author
|
As an author
|
||||||
I want to search for a page in the CMS
|
I want to search for a page in the CMS
|
||||||
|
66
tests/behat/src/FixtureContext.php
Normal file
66
tests/behat/src/FixtureContext.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\CMS\Tests\Behaviour;
|
||||||
|
|
||||||
|
use SilverStripe\BehatExtension\Context\FixtureContext as BehatFixtureContext;
|
||||||
|
use SilverStripe\CMS\Model\RedirectorPage;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Core\ClassInfo;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\Dev\FixtureBlueprint;
|
||||||
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context used to create fixtures in the SilverStripe ORM.
|
||||||
|
*/
|
||||||
|
class FixtureContext extends BehatFixtureContext
|
||||||
|
{
|
||||||
|
protected function scaffoldDefaultFixtureFactory()
|
||||||
|
{
|
||||||
|
$factory = parent::scaffoldDefaultFixtureFactory();
|
||||||
|
|
||||||
|
// Use blueprints which auto-publish all subclasses of SiteTree
|
||||||
|
foreach (ClassInfo::subclassesFor(SiteTree::class) as $class) {
|
||||||
|
$blueprint = Injector::inst()->create(FixtureBlueprint::class, $class);
|
||||||
|
$blueprint->addCallback('afterCreate', function ($obj, $identifier, &$data, &$fixtures) {
|
||||||
|
/** @var SiteTree $obj */
|
||||||
|
$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
});
|
||||||
|
$factory->define($class, $blueprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find or create a redirector page and link to another existing page.
|
||||||
|
* Example: Given a "page" "My Redirect" which redirects to a "page" "Page 1"
|
||||||
|
*
|
||||||
|
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" (:?which )?redirects to (?:(an|a|the) )"(?<targetType>[^"]+)" "(?<targetId>[^"]+)"$/
|
||||||
|
* @param string $type
|
||||||
|
* @param string $id
|
||||||
|
* @param string $targetType
|
||||||
|
* @param string $targetId
|
||||||
|
*/
|
||||||
|
public function stepCreateRedirectorPage($type, $id, $targetType, $targetId)
|
||||||
|
{
|
||||||
|
$class = RedirectorPage::class;
|
||||||
|
$targetClass = $this->convertTypeToClass($targetType);
|
||||||
|
|
||||||
|
$targetObj = $this->fixtureFactory->get($targetClass, $targetId);
|
||||||
|
if (!$targetObj) {
|
||||||
|
$targetObj = $this->fixtureFactory->get($targetClass, $targetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = array('LinkToID' => $targetObj->ID);
|
||||||
|
/** @var RedirectorPage $obj */
|
||||||
|
$obj = $this->fixtureFactory->get($class, $id);
|
||||||
|
if ($obj) {
|
||||||
|
$obj->update($fields);
|
||||||
|
} else {
|
||||||
|
$obj = $this->fixtureFactory->createObject($class, $id, $fields);
|
||||||
|
}
|
||||||
|
$obj->write();
|
||||||
|
$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
}
|
||||||
|
}
|
35
tests/behat/src/LoginContext.php
Normal file
35
tests/behat/src/LoginContext.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\CMS\Tests\Behaviour;
|
||||||
|
|
||||||
|
use Page;
|
||||||
|
use SilverStripe\BehatExtension\Context\LoginContext as BehatLoginContext;
|
||||||
|
|
||||||
|
class LoginContext extends BehatLoginContext
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Check if the user can edit a page
|
||||||
|
*
|
||||||
|
* Example: Then pages should be editable by "Admin"
|
||||||
|
* Then pages should not be editable by "Admin"
|
||||||
|
*
|
||||||
|
* @Then /^pages should( not? |\s*)be editable by "([^"]*)"$/
|
||||||
|
*/
|
||||||
|
public function pagesShouldBeEditableBy($negative, $permCode)
|
||||||
|
{
|
||||||
|
// Reset permission cache
|
||||||
|
$page = Page::get()->First();
|
||||||
|
assertNotNull($page, 'A page exists');
|
||||||
|
$email = "{$permCode}@example.org";
|
||||||
|
$password = 'Password!456';
|
||||||
|
$member = $this->generateMemberWithPermission($email, $password, $permCode);
|
||||||
|
$canEdit = strstr($negative, 'not') ? false : true;
|
||||||
|
|
||||||
|
if ($canEdit) {
|
||||||
|
assertTrue($page->canEdit($member), 'The member can edit this page');
|
||||||
|
} else {
|
||||||
|
assertFalse($page->canEdit($member), 'The member cannot edit this page');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace SilverStripe\Cms\Test\Behaviour;
|
namespace SilverStripe\CMS\Tests\Behaviour;
|
||||||
|
|
||||||
use Behat\Behat\Context\ClosuredContextInterface;
|
use Behat\Behat\Context\Context;
|
||||||
use Behat\Behat\Context\TranslatedContextInterface;
|
use SilverStripe\BehatExtension\Context\MainContextAwareTrait;
|
||||||
use Behat\Behat\Context\BehatContext;
|
|
||||||
use Behat\Behat\Context\Step;
|
|
||||||
use Behat\Behat\Event\StepEvent;
|
|
||||||
use Behat\Behat\Exception\PendingException;
|
|
||||||
use Behat\Mink\Driver\Selenium2Driver;
|
|
||||||
use Behat\Gherkin\Node\PyStringNode;
|
|
||||||
use Behat\Gherkin\Node\TableNode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context used to create fixtures in the SilverStripe ORM.
|
* Context used to create fixtures in the SilverStripe ORM.
|
||||||
*/
|
*/
|
||||||
class ThemeContext extends BehatContext
|
class ThemeContext implements Context
|
||||||
{
|
{
|
||||||
|
use MainContextAwareTrait;
|
||||||
|
|
||||||
protected $restoreFiles = array();
|
protected $restoreFiles = array();
|
||||||
protected $restoreDirectories = array();
|
protected $restoreDirectories = array();
|
||||||
@ -25,6 +19,7 @@ class ThemeContext extends BehatContext
|
|||||||
* Create a test theme
|
* Create a test theme
|
||||||
*
|
*
|
||||||
* @Given /^a theme "(?<theme>[^"]+)"/
|
* @Given /^a theme "(?<theme>[^"]+)"/
|
||||||
|
* @param string $theme
|
||||||
*/
|
*/
|
||||||
public function stepCreateTheme($theme)
|
public function stepCreateTheme($theme)
|
||||||
{
|
{
|
||||||
@ -41,6 +36,9 @@ class ThemeContext extends BehatContext
|
|||||||
* Create a template within a test theme
|
* Create a template within a test theme
|
||||||
*
|
*
|
||||||
* @Given /^a template "(?<template>[^"]+)" in theme "(?<theme>[^"]+)" with content "(?<content>[^"]+)"/
|
* @Given /^a template "(?<template>[^"]+)" in theme "(?<theme>[^"]+)" with content "(?<content>[^"]+)"/
|
||||||
|
* @param string $template
|
||||||
|
* @param string $theme
|
||||||
|
* @param string $content
|
||||||
*/
|
*/
|
||||||
public function stepCreateTemplate($template, $theme, $content)
|
public function stepCreateTemplate($template, $theme, $content)
|
||||||
{
|
{
|
Loading…
Reference in New Issue
Block a user