提交 4d7ddb2a 编写于 作者: 伟伟权

重构1.0版本😎

上级 5a062d8a
......@@ -15,29 +15,5 @@
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=1e0a0e96d4ade8189745bcdd2946c25f
#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
#TRUSTED_HOSTS='^(localhost|example\.com)$'
APP_SECRET=9e2d676e241eddebe293fc439b2574b8
###< symfony/framework-bundle ###
###> symfony/mailer ###
# MAILER_DSN=smtp://localhost
###< symfony/mailer ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8"
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7
###< doctrine/doctrine-bundle ###
###> nelmio/cors-bundle ###
CORS_ALLOW_ORIGIN=^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$
###< nelmio/cors-bundle ###
###> symfony/lock ###
# Choose one of the stores below
# postgresql+advisory://db_user:db_password@localhost/db_name
LOCK_DSN=semaphore
###< symfony/lock ###
# define your env variables for the test env here
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
......@@ -9,25 +9,7 @@
/vendor/
###< symfony/framework-bundle ###
###> symfony/phpunit-bridge ###
.phpunit
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###
###> local ###
###> phpstorm & MacOS ###
.idea
.DS_Store
node_modules
composer.local.json
public/vendor/
public/uploads/
###< local ###
###> liip/imagine-bundle ###
/public/media/cache/
###< liip/imagine-bundle ###
###> phpunit/phpunit ###
/phpunit.xml
.phpunit.result.cache
###< phpunit/phpunit ###
###< phpstorm & MacOS ###
\ No newline at end of file
1.0.0(2021-09-05)
1. 使用全新Symfony5进行重构。
\ No newline at end of file
#CHANGELOG
0.1.1:
1. fosckeditorbundle 添加文件图片拖拽上传和图片上传功能
2. 用户组权限调整,不同用户组可设置不同的ckeditor工具栏配置
3. 文本、长文本字段默认过滤所有HTML标签
0.1.2:
1. 邮件发送fromAddress去除硬编码,改为在teebb_core.yaml文件中手动配置且必须配置
2. Dashboard控制台首页blocks、侧边栏菜单使用单独yaml文件配置
0.1.3
1. Kernel版本号修改
2. block ContentsBlockService 添加类型参数用于获取指定类型内容列表
3. 添加"用户字段"菜单,对用户字段进行管理,用户默认增加头像字段。用户表单调整。
4. 添加Twig函数: show_content_all_fields 获取并显示当前内容所有字段;get_content_field 获取当前内容指定字段数据
5. 添加Twig macro方法:showContentAllFieldsData 用此macro可方便的显示当前内容所有字段
0.1.4
1. teebb.core.block.content Block调整过滤条件
0.1.5
1. 增加字段缓存
2. 前台页面增加DOM块级缓存
3. 系统设置优化
4. bug修复
0.1.6
1. prod环境下动态mapping字段,doctrine需要auto_generate_proxy_classes设置为true
0.1.7
1. prod环境下动态mapping字段,doctrine需要auto_generate_proxy_classes设置为EVAL对性能更好些
0.1.8
1. ckeditor添加codesnippet插件,修改ckeditor配置
0.1.9
1. ckeditor添加markdown插件,修改ckeditor配置
0.1.10
1. Symfony5.3支持
2. 使用新的用户登录认证系统
\ No newline at end of file
MIT License
Copyright (c) 2020 teebbstudios
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
# TEEBB 0.x版本进入维护状态。即将重构代码,敬请期待。
##### doctrine-bundle 2.3以上版本使用PhpArrayCache缓存ORM注解Metadata,会引起TEEBB在生产环境下重大BUG。
```php
// 需要修改 doctrine-bundle 源码解决此问题
// vendor/doctrine/doctrine-bundle/DependencyInjection/DoctrineExtension.php
protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container)
{
$this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container);
$this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container);
$this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container);
if ($container->getParameter('kernel.debug')) {
return;
}
// 需要注释下面一行代码解决BUG
// $this->registerMetadataPhpArrayCaching($entityManager['name'], $container);
}
```
# TEEBB内容管理系统使用文档
TEEBB是基于Symfony框架开发的一款可自由扩展的内容管理系统。您可以自由使用本软件来构建您的博客、网站等应用。本项目将会长期开发维护,欢迎使用。
TEEBB的原理设计参考了著名的Drupal,注重内容的扩展性与功能性,因为Drupal的学习曲线太高了,于是我开发了TEEBB,它更轻量也更容易上手。目前0.x版本已实现了内容管理和一些基本功能,
注重内容类型网站的开发。在后续版本中将实现I18N(国际化),RESTful API,消息队列,缓存等等功能。如果您是Symfony初学者,TEEBB用到了Symfony的多个组件,是个不错的学习案例。
欢迎您进行Fork,并提交Pull Request,我们一起开发维护TEEBB,如果后续TEEBB有获利计划,我将优先通知您参与并获取相应利益。
![alt TEEBB控制台](docs/teebb_images/teebb-dashboard.png "TEEBB控制台")
## 看 [TEEBB基础视频教程](https://www.teebb.com/course-detail/teebb-begin)
### 1.TEEBB的下载和安装说明
TEEBB的运行环境:
PHP 7.2.5及以上、Mysql 5.7.x、推荐安装:Symfony 命令行工具,详见:www.symfony.com
```
#下载TEEBB
composer create-project teebbstudios/teebb acme
cd acme
#安装JS库
npm install
gulp build
#安装CKEDITOR 资源包
symfony console ckeditor:install
#TEEBB资源安装
symfony console assets:install public --symlink
#复制.env文件到.env.local, 并修改数据库DATABASE_URL及MAILER_DSN信息
cp .env .env.local
#初始化数据库
symfony console teebb:database:init -d
#运行服务器
symfony serve
#打开页面登录管理页面
open http://127.0.0.1:8000/admin
```
> 初始管理员账号:admin
> 初始管理员密码:admin
### 2.TEEBB的版本号及开发周期说明
TEEBB的版本号遵循语义化版本格式:主版本号.次版本号.修订号
TEEBB的次版本号(例如:0.2.x,0.3.x)发布周期:至少每四个月发布一次。
TEEBB的修订版本号(例如:0.1.1,0.1.2)发布周期:至少每三个星期发布一次。
> Tips:目前TEEBB 0.x版本的核心功能bundle仅包含在teebbstudios/core-bundle,并不利于后期维护,因此将在1.x版本重构TEEBB。
### 3.TEEBB的使用介绍
TEEBB的主题是内容管理,但是现实中各种各样不同类型的内容太多了,为了满足各种不同的需求以及实现灵活的扩展性,原理上我借鉴了国外的两个著名开源项目Drupal和Godot。
做个假设:
> 在另一个平行时空,我不是程序员而是一个名不见经传的在线小报记者,每天都忙于收集各种新闻,并发表在新闻网站上。
> 我的新闻网站分为很多不同的栏目,有的栏目要求必须添加新闻图片及文字,有的栏目只用上传新闻文字,有的栏目只用上传新闻图片。
> 传统的内容管理系统是怎么做的呢?
> 它可能预先定义了一个统一的新闻格式,有标题、主体(Body)、封面图片等等所有可能用到的东西,然后我们创建对应的栏目分类词,再根据需要把新添加的新闻指定到对应的栏目中,这是一个好办法。但是我觉得这样灵活性及扩展性就不那么好了。
> 如果我们能把新闻中所用到的标题、主体(Body)、图片等等信息抽取成不同类型的组件(我把这种组件称为“字段”,后面都称为“字段”),然后将用到的字段组合成不同的内容类型,这样会在最大程度上实现灵活性及扩展性。
> 在TEEBB中,目前实现了内容、评论、分类、用户四种bundle包。这四种bundle的具体实现类型都可以组合不同的字段以实现不同的需求。
[3.1 内容bundle](docs/3-1conent-bundle.md)
- [3.1.1 创建第一个内容类型](docs/3-1conent-bundle.md)
[3.2 字段](docs/3-2field.md)
- [3.2.1 TEEBB内置的字段](docs/3-2field.md)
- [3.2.2 TEEBB中常用字段的设置](docs/3-2field.md)
[3.3 内容](docs/3-3conent.md)
- [3.3.1 添加内容](docs/3-3conent.md)
- [3.3.2 内容的查询(未实现,后续版本实现)](docs/3-3conent.md)
[3.4 分类bundle](docs/3-4taxonomy-bundle.md)
- [3.4.1 创建分类类型](docs/3-4taxonomy-bundle.md)
- [3.4.2 分类类型中字段的设置](docs/3-4taxonomy-bundle.md)
- [3.4.3 添加分类词汇](docs/3-4taxonomy-bundle.md)
- [3.4.4 分类字段的使用](docs/3-4taxonomy-bundle.md)
[3.5 评论bundle](docs/3-5comment-bundle.md)
- [3.5.1 创建评论类型](docs/3-5comment-bundle.md)
- [3.5.2 评论类型中字段的设置](docs/3-5comment-bundle.md)
- [3.5.3 评论字段的使用](docs/3-5comment-bundle.md)
- [3.5.4 管理评论内容](docs/3-5comment-bundle.md)
[3.6 用户与用户组](docs/3-6user-bundle-group.md)
- [3.6.1 用户的概念](docs/3-6user-bundle-group.md)
- [3.6.2 用户的字段管理(后续版本实现)](docs/3-6user-bundle-group.md)
- [3.6.3 用户组及权限](docs/3-6user-bundle-group.md)
[3.7 菜单系统](docs/3-7menu-system.md)
- [3.7.1 创建第一个菜单](docs/3-7menu-system.md)
- [3.7.2 菜单的显示](docs/3-7menu-system.md)
[3.8 常用Twig函数及全局变量(将会有视频教程讲解,敬请期待)](docs/3-8twig-front-route.md)
### 4.基于TEEBB的开发(将有免费视频教程,敬请期待)
TEEBB的设计就是要高扩展性,因此我们可以在TEEBB上进行二次开发。TEEBB默认提供了内容bundle、分类bundle、评论bundle、用户bundle。
如果有一天有个需求开发一个小型的在线商城。我们可以利用图像字段作为商品的图像信息,利用布尔值字段做为商品的上下架信息、全新或二手信息,利用小数字段为商品的价格信息等等。这样我们可以创建一个bundle作为SKU对象,再创建一个bundle做为商品对象并两者进行关联以实现商城功能。
不止商城,其他类型的应用也完全可以在TEEBB上进行二次开发。
Wooooo!这就是我对TEEBB的设想,如果我们基于TEEBB的二次开发能做些bundle产品并出售赚取一些利润,那就再好不过了。:)
4.1 创建自定义字段
- 4.1.1 FieldType注解介绍
- 4.1.2 实现注解中的所有的类
4.2 创建自定义bundle
- 4.2.1 EntityType注解介绍
- 4.2.2 实现EntityType中的所有类
> Tips: 此节内容较多,文字无法完全表述,将有免费的视频教程推出。基于TEEBB的开发将以视频教程的形式发布在www.teebb.com上,敬请期待。
### 5.TEEBB的Roadmap
目前TEEBB发布了0.1.0版本,仍有不少功能急需完善,暂将一些未实现功能按优先级列下,暂不公布时间线,我把其他工作做完就会完善的,表着急啊:)。
1. ckeditor编辑器的图像上传。
2. 控制台Topbar及内容列表搜索过滤。
3. 用户字段的实现及前端 分类 内容 字段的显示。
4. 用户的字段管理。
5. 缓存。
6. RESTful API, 使用api-platform开发。
7. 格式化器及ckeditor编辑器在使用时联动的优化。
8. 控制台Topbar面包屑功能。
9. bug修复。
### 6.TEEBB开源协议
本软件遵循MIT协议。如果您能在页面上注明您的应用使用了TEEBB,本人将万分感谢。:)
### 7.写在最后
本人可接Symfony开发的工作,正规公司资质,可开增值税发票,如有开发业务请与我联系。QQ/微信:443580003
\ No newline at end of file
# TEEBB正在重构,将更偏向于移动端的开发,可作为REST API服务端。敬请期待。
\ No newline at end of file
......@@ -3,40 +3,15 @@
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\ErrorHandler\Debug;
if (!in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL;
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
}
set_time_limit(0);
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
require dirname(__DIR__).'/vendor/autoload.php';
return function (array $context) {
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
if (!class_exists(Application::class)) {
throw new LogicException('You need to add "symfony/framework-bundle" as a Composer dependency.');
}
$input = new ArgvInput();
if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
}
if ($input->hasParameterOption('--no-debug', true)) {
putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0');
}
require dirname(__DIR__).'/config/bootstrap.php';
if ($_SERVER['APP_DEBUG']) {
umask(0000);
if (class_exists(Debug::class)) {
Debug::enable();
}
}
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$application = new Application($kernel);
$application->run($input);
return new Application($kernel);
};
#!/usr/bin/env php
<?php
if (!file_exists(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
exit(1);
}
if (false === getenv('SYMFONY_PHPUNIT_DIR')) {
putenv('SYMFONY_PHPUNIT_DIR='.__DIR__.'/.phpunit');
}
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php';
{
"name": "teebbstudios/teebb",
"description": "The Teebb content management framework base on the Symfony.",
"keywords": [
"teebb",
"cms"
],
"authors": [
{
"name": "Quan Weiwei",
"email": "qww.zone@gmail.com"
}
],
"type": "project",
"license": "MIT",
"license": "proprietary",
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": "^7.2.5",
"php": ">=7.2.5",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/core": "^2.6",
"babdev/pagerfanta-bundle": "^2.4",
"composer/package-versions-deprecated": "1.11.99.3",
"doctrine/annotations": "^1.0",
"doctrine/common": "^2",
"doctrine/doctrine-bundle": "^2",
"doctrine/doctrine-migrations-bundle": "^2",
"doctrine/orm": "^2",
"friendsofsymfony/ckeditor-bundle": "^2.2",
"friendsofsymfony/jsrouting-bundle": "^2.6",
"knplabs/knp-menu-bundle": "^3.0",
"knplabs/knp-time-bundle": "^1.13",
"liip/imagine-bundle": "^2.3",
"nelmio/cors-bundle": "^2.1",
"oneup/flysystem-bundle": "^3.5",
"pagerfanta/pagerfanta": "^2.3",
"phpdocumentor/reflection-docblock": "^5.2",
"sensio/framework-extra-bundle": "^5.1",
"sonata-project/block-bundle": "^4.4",
"stof/doctrine-extensions-bundle": "^1.4",
"symfony/asset": "^5.0",
"symfony/console": "^5.0",
"symfony/dotenv": "^5.0",
"symfony/expression-language": "^5.0",
"symfony/console": "5.3.*",
"symfony/dotenv": "5.3.*",
"symfony/flex": "^1.3.1",
"symfony/form": "^5.0",
"symfony/framework-bundle": "^5.0",
"symfony/http-client": "^5.0",
"symfony/intl": "^5.0",
"symfony/mailer": "^5.0",
"symfony/mime": "^5.0",
"symfony/monolog-bundle": "^3.1",
"symfony/notifier": "^5.0",
"symfony/process": "^5.0",
"symfony/property-access": "^5.0",
"symfony/property-info": "^5.0",
"symfony/security-bundle": "^5.0",
"symfony/serializer": "^5.0",
"symfony/string": "^5.0",
"symfony/translation": "^5.0",
"symfony/twig-bundle": "^5.0",
"symfony/validator": "^5.0",
"symfony/web-link": "^5.0",
"symfony/yaml": "^5.0",
"teebbstudios/core-bundle": "^0.1.0",
"symfony/framework-bundle": "5.3.*",
"symfony/runtime": "5.3.*",
"symfony/twig-bundle": "5.3.*",
"symfony/yaml": "5.3.*",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"symfony/browser-kit": "^5.3",
"symfony/css-selector": "^5.3",
"symfony/debug-bundle": "^5.3",
"symfony/maker-bundle": "^1.0",
"symfony/phpunit-bridge": "^5.3",
"symfony/stopwatch": "^5.3",
"symfony/web-profiler-bundle": "^5.3"
"symfony/debug-bundle": "5.3.*",
"symfony/maker-bundle": "^1.33",
"symfony/monolog-bundle": "^3.0",
"symfony/stopwatch": "5.3.*",
"symfony/web-profiler-bundle": "5.3.*"
},
"config": {
"optimize-autoloader": true,
"preferred-install": {
"*": "dist"
},
......@@ -92,13 +42,9 @@
}
},
"replace": {
"paragonie/random_compat": "2.*",
"symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*",
"symfony/polyfill-php72": "*",
"symfony/polyfill-php71": "*",
"symfony/polyfill-php70": "*",
"symfony/polyfill-php56": "*"
"symfony/polyfill-php72": "*"
},
"scripts": {
"auto-scripts": {
......@@ -118,7 +64,7 @@
"extra": {
"symfony": {
"allow-contrib": false,
"require": "^5.0"
"require": "5.3.*"
}
}
}
此差异已折叠。
<?php
use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__).'/vendor/autoload.php';
if (!class_exists(Dotenv::class)) {
throw new LogicException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.');
}
// Load cached env vars if the .env.local.php file exists
// Run "composer dump-env prod" to create it (requires symfony/flex >=1.2)
if (is_array($env = @include dirname(__DIR__).'/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) {
(new Dotenv(false))->populate($env);
} else {
// load all the .env files
(new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env');
}
$_SERVER += $_ENV;
$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev';
$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV'];
$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0';
......@@ -2,27 +2,10 @@
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
FOS\CKEditorBundle\FOSCKEditorBundle::class => ['all' => true],
Oneup\FlysystemBundle\OneupFlysystemBundle::class => ['all' => true],
Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true],
FOS\JsRoutingBundle\FOSJsRoutingBundle::class => ['all' => true],
BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class => ['all' => true],
Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true],
Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true],
Sonata\Form\Bridge\Symfony\SonataFormBundle::class => ['all' => true],
Sonata\BlockBundle\SonataBlockBundle::class => ['all' => true],
Teebb\CoreBundle\TeebbCoreBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
];
api_platform:
mapping:
paths: ['%kernel.project_dir%/src/Entity']
patch_formats:
json: ['application/merge-patch+json']
swagger:
versions: [3]
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '5.7'
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
doctrine_migrations:
dir_name: '%kernel.project_dir%/src/Migrations'
# namespace is arbitrary but should be different from App\Migrations
# as migrations classes should NOT be autoloaded
namespace: DoctrineMigrations
# Read the documentation: https://symfony.com/doc/current/bundles/FOSCKEditorBundle/index.html
twig:
form_themes:
- '@FOSCKEditor/Form/ckeditor_widget.html.twig'
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework:
secret: '%env(APP_SECRET)%'
#csrf_protection: true
#http_method_override: true
http_method_override: false
# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.
......@@ -9,10 +10,15 @@ framework:
handler_id: null
cookie_secure: auto
cookie_samesite: lax
storage_factory_id: session.storage.factory.native
#esi: true
#fragments: true
php_errors:
log: true
ide: phpstorm
\ No newline at end of file
when@test:
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file
# See dos how to configure the bundle: https://symfony.com/doc/current/bundles/LiipImagineBundle/basic-usage.html
liip_imagine:
# valid drivers options include "gd" or "gmagick" or "imagick"
driver: "gd"
framework:
lock: '%env(LOCK_DSN)%'
framework:
mailer:
dsn: '%env(MAILER_DSN)%'
nelmio_cors:
defaults:
origin_regex: true
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
allow_headers: ['Content-Type', 'Authorization']
expose_headers: ['Link']
max_age: 3600
paths:
'^/': null
framework:
notifier:
#chatter_transports:
# slack: '%env(SLACK_DSN)%'
# telegram: '%env(TELEGRAM_DSN)%'
#texter_transports:
# twilio: '%env(TWILIO_DSN)%'
# nexmo: '%env(NEXMO_DSN)%'
channel_policy:
# use chat/slack, chat/telegram, sms/twilio or sms/nexmo
urgent: ['email']
high: ['email']
medium: ['email']
low: ['email']
admin_recipients:
- { email: admin@example.com }
# Read the documentation: https://github.com/1up-lab/OneupFlysystemBundle/tree/master/Resources/doc/index.md
oneup_flysystem:
adapters:
default_adapter:
local:
directory: '%kernel.cache_dir%/flysystem'
filesystems:
default_filesystem:
adapter: default_adapter
alias: League\Flysystem\Filesystem
# As of Symfony 5.1, deprecations are logged in the dedicated "deprecation" channel when it exists
#monolog:
# channels: [deprecation]
# handlers:
# deprecation:
# type: stream
# channels: [deprecation]
# path: php://stderr
doctrine:
orm:
auto_generate_proxy_classes: EVAL
metadata_cache_driver:
type: pool
pool: doctrine.system_cache_pool
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system
......@@ -8,19 +8,10 @@ monolog:
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
path: php://stderr
level: debug
formatter: monolog.formatter.json
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine"]
# Uncomment to log deprecations
#deprecation:
# type: stream
# path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log"
#deprecation_filter:
# type: filter
# handler: deprecation
# max_level: info
# channels: ["php"]
framework:
router:
strict_requirements: null
framework:
router:
utf8: true
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
#default_uri: http://localhost
when@prod:
framework:
router:
strict_requirements: null
security:
encoders:
Teebb\CoreBundle\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security/experimental_authenticators.html
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
user_provider:
entity:
class: Teebb\CoreBundle\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: user_provider
logout:
path: teebb_user_logout
# 弃用,从symfony5.3版本起使用新的认证系统
# guard:
# authenticators:
# - Teebb\CoreBundle\Security\LoginFormAuthenticator
custom_authenticator: Teebb\CoreBundle\Security\AppLoginFormAuthenticator
user_checker: Teebb\CoreBundle\Security\UserChecker
login_throttling:
max_attempts: 3
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 7 days in seconds
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
sensio_framework_extra:
router:
annotations: false
sonata_form:
form_type: standard
# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html
# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/
stof_doctrine_extensions:
default_locale: '%locale%'
orm:
default:
translatable: true
timestampable: true
blameable: true
sluggable: true
tree: true
loggable: true
sortable: true
softdeleteable: true
uploadable: true
reference_integrity: true
teebb_core:
#用于自动检测并获取teebb中的注解配置
mapping:
directories: ['%kernel.project_dir%/src']
#邮件发送人配置此项必须配置,请修改为您的配置
from_email:
address: sender@quanweiwei.cn
address_name: TEEBB.COM管理员
fly_system:
service: '%fly_system_service%'
#此变量用于生成上传文件的链接
root_host_url: '%fly_system_host%/uploads'
#文本过滤器用于 文本已格式化字段,长文本已格式化字段,长文本已格式化字段带摘要
filter_settings:
strip_tags:
filter_class: Teebb\CoreBundle\TextFilter\StripTagsFilter
filter_label: teebb.core.form.strip_tags_filter
extra_form_type: Symfony\Component\Form\Extension\Core\Type\TextareaType
extra_label: teebb.core.form.strip_tags_filter_extra
extra_help: teebb.core.form.strip_tags_filter_extra_help
nl2br:
filter_class: Teebb\CoreBundle\TextFilter\Nl2brFilter
filter_label: teebb.core.form.nl2br_filter
escape_html:
filter_class: Teebb\CoreBundle\TextFilter\EscapeHtmlFilter
filter_label: teebb.core.form.escape_html_filter
#配置页面中额外的assets库,使用TemplateRegister将对应库加入页面
assets:
extra:
transliteration:
js_path: vendor/transliteration/bundle.umd.min.js
videojs:
js_path: vendor/video.js/video.min.js
css_path: vendor/video.js/video-js.min.css
sortablejs:
js_path: vendor/sortablejs/Sortable.min.js
autocompletejs:
js_path: vendor/autocomplete.js/js/autocomplete.jquery.min.js
nestable:
js_path: vendor/nestable/jquery.nestable.js
highlight:
js_path: bundles/teebbcore/ckeditor-plugins/codesnippet/lib/highlight/highlight.pack.js
css_path: bundles/teebbcore/ckeditor-plugins/codesnippet/lib/highlight/styles/dark.css
oneup_flysystem:
adapters:
public_dir_adapter:
local:
directory: '%kernel.project_dir%/public/uploads'
filesystems:
public_dir_filesystem:
adapter: public_dir_adapter
alias: League\Flysystem\Filesystem
liip_imagine:
resolvers:
flysystem_images_resolver:
flysystem:
filesystem_service: '%fly_system_service%'
root_url: '%fly_system_host%/uploads' #用于生成图像链接
cache_prefix: media/cache
visibility: public
# default cache resolver for saving thumbnails
cache: flysystem_images_resolver
loaders:
flysystem_images:
flysystem:
filesystem_service: '%fly_system_service%'
# default loader to use for all filter sets
data_loader: flysystem_images
filter_sets:
squared_thumbnail_small:
filters:
thumbnail:
size: [100, 100]
mode: outbound
allow_upscale: true
fos_ck_editor:
default_config: standard
configs:
full:
toolbar: full
extraPlugins: 'codesnippet, uploadfile, uploadimage, markdown'
uploadUrl: /file/upload
imageUploadURL: /file/upload
filebrowserUploadUrl: /file/upload
filebrowserFlashUploadUrl: /file/upload
filebrowserImageUploadUrl: /file/upload
codeSnippet_theme: 'default'
removePlugins: 'exportpdf'
standard:
toolbar: standard
extraPlugins: 'codesnippet, uploadfile, uploadimage, markdown'
uploadUrl: /file/upload
imageUploadURL: /file/upload
filebrowserUploadUrl: /file/upload
filebrowserFlashUploadUrl: /file/upload
filebrowserImageUploadUrl: /file/upload
codeSnippet_theme: 'default'
removePlugins: 'exportpdf'
basic:
toolbar: basic
removePlugins: 'exportpdf'
plugins:
codesnippet:
path: /bundles/teebbcore/ckeditor-plugins/codesnippet/
filename: plugin.js
uploadfile:
path: /bundles/teebbcore/ckeditor-plugins/uploadfile/
filename: plugin.js
uploadimage:
path: /bundles/teebbcore/ckeditor-plugins/uploadimage/
filename: plugin.js
markdown:
path: /bundles/teebbcore/ckeditor-plugins/markdown/
filename: plugin.js
toolbars:
items:
'full.tools': ['Maximize', 'ShowBlocks', 'CodeSnippet', 'Markdown']
'standard.tools': ['Maximize', 'CodeSnippet', 'Markdown']
doctrine:
orm:
entity_managers:
filters:
filters:
soft-deleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
default:
auto_mapping: true
mappings:
gedmo_translatable:
type: annotation
prefix: Gedmo\Translatable\Entity
dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Entity"
alias: GedmoTranslatable # (optional) it will default to the name set for the mapping
is_bundle: false
gedmo_translator:
type: annotation
prefix: Gedmo\Translator\Entity
dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translator/Entity"
alias: GedmoTranslator # (optional) it will default to the name set for the mapping
is_bundle: false
gedmo_loggable:
type: annotation
prefix: Gedmo\Loggable\Entity
dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Loggable/Entity"
alias: GedmoLoggable # (optional) it will default to the name set for the mapping
is_bundle: false
gedmo_tree:
type: annotation
prefix: Gedmo\Tree\Entity
dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Tree/Entity"
alias: GedmoTree # (optional) it will default to the name set for the mapping
is_bundle: false
#加入额外配置:控制台首页blocks和边栏菜单
imports:
- { resource: teebb_core/ }
\ No newline at end of file
teebb_core:
#dashboard index blocks
blocks:
- position: top
type: teebb.core.block.count #block service type
class: col-xl-3 col-md-6 mb-4 #block class
groups: [] #block show user groups
settings:
entity_class: Teebb\CoreBundle\Entity\Content #block admin. admin 对应的entity 应含有 createAt 和 updateAt 两个时间属性。
label: contents(last month) #block label
#translation_domain: #default: messages
icon: fas fa-file-alt #block icon
border: border-left-primary #block border
#property: createdAt #query object property. 要查询的Entity的属性以此属性统计数量
duration: -1 month #The count of content in a duration, must earlier than now. see php strtotime. default: -1 month
#template: #default template @TeebbCore/Block/count_block.html.twig
- position: top
type: teebb.core.block.count #block service type
class: col-xl-3 col-md-6 mb-4 #block class
groups: [] #block show user groups
settings:
entity_class: Teebb\CoreBundle\Entity\Types\Types #block admin. admin 对应的entity 应含有 createAt 和 updateAt 两个时间属性。
bundle: content
label: content_types #block label
#translation_domain: #default: messages
icon: fas fa-boxes #block icon
border: border-left-success #block border
#property: createdAt #query object property. 要查询的Entity的属性以此属性统计数量
duration: -1 month #The count of content in a duration, must earlier than now. see php strtotime. default: -1 month
#template: #default template @TeebbCore/Block/count_block.html.twig
- position: top
type: teebb.core.block.count #block service type
class: col-xl-3 col-md-6 mb-4 #block class
groups: [] #block show user groups
settings:
entity_class: Teebb\CoreBundle\Entity\User #block admin. admin 对应的entity 应含有 createAt 和 updateAt 两个时间属性。
label: users(last month) #block label
#translation_domain: #default: messages
icon: fas fa-user #block icon
border: border-left-info #block border
#property: createdAt #query object property. 要查询的Entity的属性以此属性统计数量
duration: -1 month #The count of content in a duration, must earlier than now. see php strtotime. default: -1 month
#template: #default template @TeebbCore/Block/count_block.html.twig
- position: top
type: teebb.core.block.count #block service type
class: col-xl-3 col-md-6 mb-4 #block class
groups: [] #block show user groups
settings:
entity_class: Teebb\CoreBundle\Entity\Comment #block admin. admin 对应的entity 应含有 createAt 和 updateAt 两个时间属性。
label: comments(last month) #block label
#translation_domain: #default: messages
icon: fas fa-comments #block icon
border: border-left-warning #block border
#property: createdAt #query object property. 要查询的Entity的属性以此属性统计数量
duration: -1 month #The count of content in a duration, must earlier than now. see php strtotime. default: -1 month
#template: #default template @TeebbCore/Block/count_block.html.twig
- position: left
type: teebb.core.block.text
settings:
label: Welcome to Teebb
content: Teebb Information
template: '@TeebbCore\blocks\dashboard_base_block.html.twig'
- position: left
type: teebb.core.block.types
settings:
label: Content Types
bundle: content
- position: left
type: teebb.core.block.contents
settings:
label: New users
entity_class: Teebb\CoreBundle\Entity\User
template: '@TeebbCore\blocks\new_users.html.twig'
order:
createdAt: DESC
- position: right
type: teebb.core.block.contents
settings:
label: Last contents
entity_class: Teebb\CoreBundle\Entity\Content
order:
createdAt: DESC
- position: right
type: teebb.core.block.contents
settings:
label: Last comments
entity_class: Teebb\CoreBundle\Entity\Comment
order:
createdAt: DESC
template: '@TeebbCore\blocks\last_comments.html.twig'
teebb_core:
#Dashboard 侧边栏菜单
side_menu:
content:
content:
label: Content
icon: fa-file-alt
items:
- label: Add Content
route: teebb_content_create_index
#route_params: {typeAlias: content}
groups: []
- label: Manage Content
route: teebb_content_index
groups: []
groups: []
type:
label: Type
icon: fa-boxes
items:
- label: Add Type
route: content_create
groups: []
- label: Manage Type
route: content_index
groups: []
groups: []
taxonomy:
label: Taxonomy
icon: fa-tags
items:
- label: Add Taxonomy
route: taxonomy_create
groups: []
- label: Manage Taxonomy
route: taxonomy_index
groups: []
groups: []
other:
menu:
label: Menu
icon: fa-list
items:
- label: Add Menu
route: teebb_menu_create
groups: []
- label: Manage Menu
route: teebb_menu_index
groups: []
groups: []
comment:
label: Comment
icon: fa-comment
items:
- label: Manage Comment
route: comment_index
groups: []
groups: []
user:
label: User
icon: fa-user
items:
- label: User Fields
route: user_index_field
route_params: {typeAlias: people}
groups: []
- label: Manage User
route: user_people_index
groups: []
- label: User Group
route: user_group_index
groups: []
groups: []
settings:
system:
label: System
icon: fa-tools
items:
- label: System Settings
route: teebb_update_option
route_params: {optionName: system}
groups: []
- label: Text Formatter
route: teebb_formatter_index
groups: []
groups: []
\ No newline at end of file
framework:
test: true
session:
storage_id: session.storage.mock_file
framework:
validation:
not_compromised_password: false
framework:
default_locale: '%locale%'
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- en
twig:
default_path: '%kernel.project_dir%/templates'
form_themes:
- '@FOSCKEditor/Form/ckeditor_widget.html.twig'
\ No newline at end of file
when@test:
twig:
strict_variables: true
framework:
validation:
email_validation_mode: html5
# Enables validator auto-mapping support.
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
#auto_mapping:
# App\Entity\: []
<?php
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) {
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php';
}
controllers:
resource: ../../src/Controller/
type: annotation
kernel:
resource: ../../src/Kernel.php
type: annotation
api_platform:
resource: .
type: api_platform
prefix: /api
_errors:
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
prefix: /_error
fos_js_routing:
resource: "@FOSJsRoutingBundle/Resources/config/routing/routing-sf4.xml"
when@dev:
_errors:
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
prefix: /_error
_liip_imagine:
resource: "@LiipImagineBundle/Resources/config/routing.yaml"
_teebb_core:
resource: .
type: entity_type
prefix: /admin
_teebb_content:
resource: "@TeebbCoreBundle/Resources/config/routing/content.xml"
prefix: /admin
defaults:
entity_type_service: '%entity_type.content.service%'
_teebb_comment:
resource: "@TeebbCoreBundle/Resources/config/routing/comment.xml"
# prefix: /admin
defaults:
entity_type_service: '%entity_type.comment.service%'
_teebb_taxonomy:
resource: "@TeebbCoreBundle/Resources/config/routing/taxonomy.xml"
prefix: /admin
defaults:
entity_type_service: '%entity_type.taxonomy.service%'
_teebb_user:
resource: "@TeebbCoreBundle/Resources/config/routing/user.xml"
prefix: /admin
defaults:
entity_type_service: '%entity_type.user.service%'
_teebb_file:
resource: "@TeebbCoreBundle/Resources/config/routing/file.xml"
_teebb_formatter:
resource: "@TeebbCoreBundle/Resources/config/routing/formatter.xml"
prefix: /admin
_teebb_field:
resource: "@TeebbCoreBundle/Resources/config/routing/field.xml"
prefix: /admin
_teebb_menu:
resource: "@TeebbCoreBundle/Resources/config/routing/menu.xml"
prefix: /admin
_teebb_dashborad:
resource: "@TeebbCoreBundle/Resources/config/routing/dashboard.xml"
prefix: /admin
_teebb_options:
resource: "@TeebbCoreBundle/Resources/config/routing/options.xml"
prefix: /admin
security:
resource: "@TeebbCoreBundle/Resources/config/routing/security.xml"
_teebb_front_content:
resource: "@TeebbCoreBundle/Resources/config/routing/front.xml"
\ No newline at end of file
......@@ -2,11 +2,8 @@
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
locale: 'zh_CN'
fly_system_service: oneup_flysystem.public_dir_filesystem_filesystem
fly_system_host: 'http://127.0.0.1:8000'
services:
# default configuration for services in *this* file
......@@ -17,14 +14,12 @@ services:
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
- '../src/Tests/'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
#### 3.1 内容bundle
内容bundle的主要功能是用于存储管理不同类型的内容。上文提到的新闻就由内容bundle存储管理。下文将完整实现一个新闻类型的创建及添加。
##### 3.1.1 创建第一个内容类型
Teebb已经内置了文章(article)和基本页面(page)两种内容类型,但是我们需要为我们的新闻创建新的类型,请看下图操作:
![alt 添加内容类型](teebb_images/create-content-type.png "添加内容类型")
如上图我们填写内容类型表单并点击保存并添加字段或保存按钮即可,我们在下节会详细介绍字段。
\ No newline at end of file
#### 3.2 字段
##### 3.2.1 TEEBB中的内置字段
TEEBB内置了多个字段,现将各字段用途作如下介绍:
常规类型:
- 链接:该字段会在创建内容时添加链接设置表单,在前台页面会生成对应超链接,可用于在内容中引用外部链接。
- 日期:该字段在会创建内容时添加日期设置表单,在前台页面显示日期值,并可使用此值做内容过滤。
- 布尔值:该字段在创建内容时添加两个预设值,可根据预设值做内容过滤。
- 评论:在内容显示页面底部生成评论显示及添加评论表单。
- 邮件地址:在创建内容时输入邮件地址。
文本类型:
- 文本:基本的255字符长度文本,不过滤html标签。
- 文本、已格式化: 基本的255字符长度文本,使用过滤器过滤html标签。
- 长文本:使用textarea标签输入的不限长度文本,不过滤html标签。
- 长文本、已格式化:使用textarea标签输入的不限长度文本,使用过滤器过滤html标签。
- 长文本、已格式化带摘要:使用textarea标签输入的不限长度文本,使用过滤器过滤html标签,并带内容摘要框,如果摘要框为空则在提交内容时自动生成摘要。
数值类型:
- 小数:数据库中以DECIMAL格式(精度根据字段设置)存储的小数。
- 浮点数:在数据库中以FLOAT类型存储。
- 浮点数列表:根据预设的浮点值列表,在添加内容时选择一个或多个值,并在前台显示此值。
- 整数:在Mysql数据库中以INT(11)类型存储。
- 整数列表:根据预设的整数值列表,在添加内容时选择一个或多个值,并在前台显示此值。
引用类型:
- 文件:用于文件上传,可设置文件上传大小、文件后缀等,在前台则显示文件链接。
- 分类:用于引用分类词汇对内容进行分类。
- 内容:引用其他站内内容,常见的相关文章可使用此字段。
- 用户:引用站内其他用户。(0.1.0版本暂未实现)
- 图像:类似于文件上传,仅可上传图像类型,并可图像文件大小、文件后缀等。在前台显示图像。
现在我们给”新闻类型一“内容类型添加字段。
如果您在上一步点击了“保存并添加字段“按钮,您将直接进入添加字段页面。也可以在“管理类型”-> “管理字段”->“添加字段”,给对应的类型添加字段,如下图:
![alt 管理内容类型页面](teebb_images/add-content-field.png "管理内容类型页面")
![alt 管理内容字段页面](teebb_images/add-content-field-2.png "管理内容字段页面")
我们的“新闻类型一”需要标题、主体(Body)、图片三个字段,我们可以使用内容bundle自带的标题,只需要添加新闻主体、图像字段即可。
下图我们首先设置好图像字段的标题并点击下方按钮进入字段设置页面:
![alt 添加字段](teebb_images/add-image-field.png "添加字段")
##### 3.2.2 TEEBB中常用字段的设置
TEEBB中不同类型的字段有不同的设置,通用部分有:
字段的标题:用于在添加内容表单或前台显示的字段标题。
别名:用于获取字段的值或在URL中显示,此值必须唯一。
字段的描述:简短描述字段的用途,用于添加内容表单行的帮助文本。
数量限制:可设置字段表单的数量,也可设置为不限制。
是否显示字段标题:用于在前台内容显示时,是否显示当前字段的标题。
现在,我们对前文新闻图片字段进行设置,我们可能需要上传多张图片,则可以在下图设置字段数量或修改为不限制,其他设置按照默认即可,设置完成点击“更新字段“按钮:
![alt 图像字段设置](teebb_images/image-field-settings.png "图像字段设置")
对于新闻主体(Body),我们可以使用“长文本、已格式化带摘要”字段。按照上述步骤添加新闻主体字段,字段设置按照默认设置即可。
> 字段设置一定要点击"更新字段"按钮,在此步骤TEEBB将为每个字段单独创建一张数据库表。否则将出现数据存储问题。
#### 3.3 内容
##### 3.3.1 添加内容
我们的“新闻类型一“内容类型已经创建完成,现在我将发表我的第一篇新闻。
点击“内容”-> “添加内容”->“新闻类型一”:
![alt 添加内容](teebb_images/add-content.png "添加内容")
Bingo!您将会发现添加内容表单将各字段的表单都自动添加了进来,下面我们将新闻的内容填写进去,点击保存即可完成我们的第一篇新闻。
![alt 添加内容表单](teebb_images/new-content-form.png "添加内容表单")
> Tips:您可能想先写新闻主体,再上传图片。我们只需调整字段显示顺序即可:
> 点击:“管理类型”->“新闻类型一”->“管理字段显示”,拖动左侧“十字箭头”即可调整字段顺序。记得点击“保存”。
![alt 调整字段顺序](teebb_images/modify-field-index.png "调整字段顺序")
##### 3.3.2 内容的查询(未实现,后续版本实现)
\ No newline at end of file
#### 3.4 分类bundle
分类bundle和内容bundle类似,不同的是分类bundle管理的是分类词汇(Term),不同的分类类型可以包含不同的字段。
举例:常见的商城类APP分类中每个分类可能需要配一个小图片,我们可以创建分类类型,给分类类型添加图像字段以实现此效果。
##### 3.4.1 创建分类类型
和创建内容类型相似,点击:“添加分类”-> “保存”或 “保存并添加字段”。分类的别名将用于URL中也要全局唯一。
##### 3.4.2 分类类型中字段的设置
分类的字段和内容类型中的字段设置操作一致。
##### 3.4.3 添加分类词汇
当我们的内容类型中使用了“分类”字段,则需要首先在对应的分类类型下添加分类词汇。点击“管理分类”->选择对应的分类类型->“管理词汇”->“添加词汇”。如下图操作:
![alt 管理分类](teebb_images/manage-taxonomy.png "管理分类")
![alt 添加分类词汇](teebb_images/add-term.png "添加分类词汇")
##### 3.4.4 分类字段的使用
在添加完几个词汇后,现在我们想给之前的“新闻分类一”添加一个分类引用。“内容”->“管理类型”->“管理字段”->“添加字段”->选择“分类”字段。如下图:
![alt 添加分类字段](teebb_images/add-taxonomy-field.png "添加分类字段")
![alt 分类字段设置](teebb_images/taxonomy-field-settings.png "分类字段设置")
> Tips: 分类字段设置图片中1号标记请选择要引用的分类类型。2号标记功能暂未实现,请忽略。
设置好分类字段后,我们重新添加/编辑内容。Wooooo! 分类字段的表单行已经出现了,我们输入相关的分类词汇,会下拉提示对应的词汇,并点击保存。
![alt 分类字段表单](teebb_images/taxonomy-field.png "分类字段表单")
#### 3.5 评论bundle
评论bundle和内容bundle、分类bundle操作基本相同。
##### 3.5.1 创建评论类型
TEEBB已经内置了“默认评论”类型。您也可以创建不同类型的评论。操作方法与创建内容类型相同。
##### 3.5.2 评论类型中字段的设置
评论类型中字段的设置与前几节相同。
##### 3.5.3 评论字段的使用
现在我们给“新闻类型一”的内容添加评论功能。和之前操作一样,给“新闻类型一”添加“评论”字段。“类型”->“管理类型”->“新闻类型一”->“管理字段”->“添加字段”->选择“评论”字段。
> Tips:“评论”类型字段的显示顺序永远在内容页面最底部,多个“评论”字段则按字段顺序依次排列到页面底部。
![alt 评论字段设置](teebb_images/comment-field-settings.png "评论字段设置")
设置完“评论字段”,我们需要为每篇内容单独打开评论。因此,请您在创建内容类型时确定是否需要评论功能,以免不必要的工作。
在新建/编辑内容页面您将看到下图所示“评论”字段的表单:
![alt 内容中的评论字段](teebb_images/content-comment-setting.png "内容中的评论字段")
> Tips: 评论功能只在前台页面显示。TEEBB默认只有已注册登录的用户可以发表评论,且评论内容经过审核后才可显示。
##### 3.5.4 管理评论内容
“评论”->“管理评论类型”->“管理评论”。您可以在此页面对已提交的评论进行操作。
![alt 管理评论](teebb_images/manage-submit-commtents.png "管理评论")
\ No newline at end of file
#### 3.6 用户与用户组
##### 3.6.1 用户的概念
TEEBB中的用户(User)也用到了bundle,可以增加修改字段以实现用户信息的扩展性,但是用户只有默认一种类型。
##### 3.6.2 用户的字段设置(后续版本实现)
##### 3.6.3 用户组及权限
在TEEBB中我们使用用户组也管理用户的权限,如果修改了用户所在的用户组,则用户的roles属性也会相应改变,此功能主是要为了方便的使用Symfony中的Security组件,对用户访问进行控制。
![alt 管理用户组](teebb_images/user-group-role.png "管理用户组")
![alt 对应组的用户都将有对应的roles](teebb_images/user-table-roles.png "对应组的用户都将有对应的roles")
#### 3.7 菜单系统
菜单系统是我的最爱,虽然通常一个网站用到菜单不多,但我仍花了些心思在菜单上以使菜单能够更加灵活。菜单系统的设计参考了wordpress的菜单。
菜单功能的实现使用了KnpMenuBundle,这个bundle功能很全面,是做菜单系统的首选。
##### 3.7.1 创建第一个菜单
点击“菜单”->“添加菜单”->输入菜单名、菜单别名、菜单描述->保存。和其他的别名一样,菜单别名也必须唯一,后期展示菜单时将用到菜单别名。
![alt 添加菜单](teebb_images/add-menu.png "添加菜单")
点击“菜单”->“管理菜单”->选择对应菜单“管理菜单项”,将进入菜单项编辑页面。
![alt 管理菜单项页面](teebb_images/manage-menu.png "管理菜单项页面")
选择左侧菜单项点击“添加到菜单”即可将菜单项添加到菜单。
1号标记:可以对菜单名称进行修改,但菜单别名不会修改。
2号标记:鼠标放置在菜单项时会呈现“十字箭头”,可拖动菜单项实现菜单项的顺序和父子关系调整。
3号标记:可以对菜单项链接名称进行调整。
最后记得点击“保存菜单“按钮。这一步很关键。:)
##### 3.7.2 菜单的显示
我们的菜单系统使用了KnpMenuBundle,我们使用KnpMenuBundle的Twig函数来显示菜单。
```twig
{#此处“demo“换成我们的菜单别名即可。#}
{{ knp_menu_render('demo') }}
```
![alt 测试菜单的显示](teebb_images/menu-show.png "测试菜单的显示")
如果你需要使用自己的菜单模板管理菜单的显示,可以给knp_menu_render方法添加template参数。我们的边栏菜单便使用了模板。
```twig
{{ knp_menu_render('demo', {template: 'template.html.twig'}) }}
```
更多的使用方法请参考KnpMenuBundle文档。https://symfony.com/doc/current/bundles/KnpMenuBundle/index.html
#### 3.8 常用Twig函数及全局变量(将会有视频教程讲解,敬请期待)
很可惜,TEEBB并没有设计模板系统。如果你经常用Twig,会发现使用Twig也能很快速的完成前端页面模板。
TEEBB中已经有了一些前台页面的Route路径,使用命令:
```
symfony console debug:route
```
| Route | Path | 描述 |
| ---- | ---- | ---- |
| teebb_user_login | /login | 用户登录 |
| teebb_user_logout | /logout | 用户退出 |
| teebb_user_register | /register | 用户注册 |
| teebb_user_resetting_request | /resetting | 重置密码 |
| teebb_content_show | /content/{slug} | 内容显示 |
| teebb_types_contents | /types/{typeAlias}/contents | 显示某内容类型下所有内容 |
| teebb_taxonomy_contents | /taxonomy/{slug}/contents | 显示某分类词汇所有内容 |
TEEBB提供了Twig全局变量:teebb_core 用于获取系统信息。使用如下方法可获取TEEBB的系统设置信息:
```twig
{{teebb_core.optionValue('system')}}
```
> Tips:更多系统设置信息将在后续版本中更新。
控制台首页有一些内容及评论的列表块,使用了SonataBlockBundle,这些Block是公用的,可以在前台页面使用到的。后续我将在视频教程中演示。
\ No newline at end of file
"use strict";
// Load plugins
const autoprefixer = require("gulp-autoprefixer");
const cleanCSS = require("gulp-clean-css");
const del = require("del");
const gulp = require("gulp");
const header = require("gulp-header");
const merge = require("merge-stream");
const plumber = require("gulp-plumber");
const rename = require("gulp-rename");
const sass = require("gulp-sass");
const uglify = require("gulp-uglify");
// Load package.json for banner
const pkg = require('./package.json');
// Set the banner content
const banner = ['/*!\n',
' * Start Bootstrap - <%= pkg.title %> v<%= pkg.version %> (<%= pkg.homepage %>)\n',
' * Copyright 2013-' + (new Date()).getFullYear(), ' <%= pkg.author %>\n',
' * Licensed under <%= pkg.license %> (https://github.com/BlackrockDigital/<%= pkg.name %>/blob/master/LICENSE)\n',
' */\n',
'\n'
].join('');
// Clean vendor
function clean() {
return del(["./public/vendor/"]);
}
// Bring third party dependencies from node_modules into vendor directory
function modules() {
// autocomplete JS
var autocomplete = gulp.src('./node_modules/autocomplete.js/dist/*')
.pipe(gulp.dest('./public/vendor/autocomplete.js/js'));
// Bootstrap JS
var bootstrapJS = gulp.src('./node_modules/bootstrap/dist/js/*')
.pipe(gulp.dest('./public/vendor/bootstrap/js'));
// Bootstrap SCSS
var bootstrapSCSS = gulp.src('./node_modules/bootstrap/scss/**/*')
.pipe(gulp.dest('./public/vendor/bootstrap/scss'));
// ChartJS
var chartJS = gulp.src('./node_modules/chart.js/dist/*.js')
.pipe(gulp.dest('./public/vendor/chart.js'));
// dataTables
var dataTables = gulp.src([
'./node_modules/datatables.net/js/*.js',
'./node_modules/datatables.net-bs4/js/*.js',
'./node_modules/datatables.net-bs4/css/*.css'
])
.pipe(gulp.dest('./public/vendor/datatables'));
// Font Awesome
var fontAwesome = gulp.src('./node_modules/@fortawesome/**/*')
.pipe(gulp.dest('./public/vendor'));
// jQuery Easing
var jqueryEasing = gulp.src('./node_modules/jquery.easing/*.js')
.pipe(gulp.dest('./public/vendor/jquery-easing'));
// jQuery
var jquery = gulp.src([
'./node_modules/jquery/dist/*',
'!./node_modules/jquery/dist/core.js'
])
.pipe(gulp.dest('./public/vendor/jquery'));
var sweetalert2 = gulp.src([
'./node_modules/sweetalert2/dist/*',
])
.pipe(gulp.dest('./public/vendor/sweetalert2'));
var sortableJs = gulp.src([
'./node_modules/sortablejs/*.js',
])
.pipe(gulp.dest('./public/vendor/sortablejs'));
var bootstrapSelect = gulp.src([
'./node_modules/bootstrap-select/dist/**/*',
])
.pipe(gulp.dest('./public/vendor/bootstrap-select'));
var transliteration = gulp.src([
'./node_modules/transliteration/dist/browser/*',
])
.pipe(gulp.dest('./public/vendor/transliteration'));
var dropzone = gulp.src([
'./node_modules/dropzone/dist/**/*',
])
.pipe(gulp.dest('./public/vendor/dropzone'));
var fabric = gulp.src([
'./node_modules/fabric/dist/**/*',
])
.pipe(gulp.dest('./public/vendor/fabric'));
var tuiCodeSnippet = gulp.src([
'./node_modules/tui-code-snippet/dist/**/*',
])
.pipe(gulp.dest('./public/vendor/tui-code-snippet'));
var tuiColorPicker = gulp.src([
'./node_modules/tui-color-picker/dist/**/*',
])
.pipe(gulp.dest('./public/vendor/tui-color-picker'));
var tuiImageEditor = gulp.src([
'./node_modules/tui-image-editor/dist/**/*',
])
.pipe(gulp.dest('./public/vendor/tui-image-editor/dist'));
var tuiImageEditorExamples = gulp.src([
'./node_modules/tui-image-editor/examples/**',
])
.pipe(gulp.dest('./public/vendor/tui-image-editor/examples'));
var fileSaver = gulp.src([
'./node_modules/file-saver/dist/**/*',
])
.pipe(gulp.dest('./public/vendor/file-saver'));
var videoJs = gulp.src([
'./node_modules/video.js/dist/**/*',
])
.pipe(gulp.dest('./public/vendor/video.js'));
var nestable = gulp.src([
'./node_modules/nestable/*',
])
.pipe(gulp.dest('./public/vendor/nestable'));
return merge(autocomplete, bootstrapJS, bootstrapSCSS, chartJS, dataTables, fontAwesome, jquery, jqueryEasing,
sweetalert2, sortableJs, bootstrapSelect, transliteration, dropzone, fabric, tuiCodeSnippet, tuiColorPicker,
fileSaver, tuiImageEditor, tuiImageEditorExamples, videoJs, nestable,
);
}
// CSS task
function css() {
return gulp
.src("./public/bundles/teebbcore/scss/**/*.scss")
.pipe(plumber())
.pipe(sass({
outputStyle: "expanded",
includePaths: "./node_modules",
}))
.on("error", sass.logError)
.pipe(autoprefixer({
cascade: false
}))
.pipe(header(banner, {
pkg: pkg
}))
.pipe(gulp.dest("./public/bundles/teebbcore/css"))
.pipe(rename({
suffix: ".min"
}))
.pipe(cleanCSS())
.pipe(gulp.dest("./public/bundles/teebbcore/css"))
}
// JS task
function js() {
return gulp
.src([
'./public/bundles/teebbcore/js/*.js',
'./public/bundles/teebbcore/js/**/*.js',
'!./public/bundles/teebbcore/js/*.min.js',
'!./public/bundles/teebbcore/js/**/*.min.js',
])
.pipe(uglify())
.pipe(header(banner, {
pkg: pkg
}))
.pipe(rename({
suffix: '.min'
}))
.pipe(gulp.dest('./public/bundles/teebbcore/js'))
}
// Define complex tasks
const vendor = gulp.series(clean, modules);
const build = gulp.series(vendor, gulp.parallel(css, js));
// Export tasks
exports.css = css;
exports.js = js;
exports.clean = clean;
exports.vendor = vendor;
exports.build = build;
exports.default = build;
此差异已折叠。
{
"title": "SB Admin 2",
"name": "startbootstrap-sb-admin-2",
"version": "4.0.4",
"scripts": {
"postinstall": "npm install fabric@1.7.22 --no-optional"
},
"description": "An open source Bootstrap 4 admin theme.",
"keywords": [
"css",
"sass",
"html",
"responsive",
"theme",
"template",
"admin",
"app"
],
"homepage": "https://startbootstrap.com/template-overviews/sb-admin-2",
"bugs": {
"url": "https://github.com/BlackrockDigital/startbootstrap-sb-admin-2/issues",
"email": "feedback@startbootstrap.com"
},
"license": "MIT",
"author": "Start Bootstrap",
"contributors": [
"David Miller (http://davidmiller.io/)"
],
"repository": {
"type": "git",
"url": "https://github.com/BlackrockDigital/startbootstrap-sb-admin-2.git"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.8.2",
"autocomplete.js": "^0.37.1",
"bootstrap": "^4.3.1",
"bootstrap-select": "^1.13.10",
"chart.js": "^2.8.0",
"datatables.net-bs4": "^1.10.19",
"dropzone": "^5.5.1",
"fabric": "^1.7.22",
"file-saver": "^2.0.2",
"jquery": "^3.5.1",
"jquery.easing": "^1.4.1",
"nan": "^2.14.0",
"nestable": "^0.2.0",
"popper.js": "^1.15.0",
"sortablejs": "^1.9.0",
"sweetalert2": "^8.11.7",
"transliteration": "^2.1.3",
"tui-code-snippet": "^1.5.1",
"tui-color-picker": "^2.2.3",
"tui-image-editor": "^3.5.2",
"video.js": "^7.5.4"
},
"devDependencies": {
"del": "^4.1.1",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^6.1.0",
"gulp-clean-css": "^4.2.0",
"gulp-header": "^2.0.7",
"gulp-plumber": "^1.2.1",
"gulp-rename": "^1.4.0",
"gulp-sass": "^4.0.2",
"gulp-uglify": "^3.0.2",
"merge-stream": "^1.0.1"
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="bin/.phpunit/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
>
<php>
<ini name="error_reporting" value="-1" />
<server name="APP_ENV" value="test" force="true" />
<server name="SHELL_VERBOSITY" value="-1" />
<server name="SYMFONY_PHPUNIT_REMOVE" value="" />
<server name="SYMFONY_PHPUNIT_VERSION" value="7.5" />
</php>
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
</listeners>
</phpunit>
<?php
use App\Kernel;
use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\HttpFoundation\Request;
require dirname(__DIR__).'/config/bootstrap.php';
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
if ($_SERVER['APP_DEBUG']) {
umask(0000);
Debug::enable();
}
if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) {
Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST);
}
if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) {
Request::setTrustedHosts([$trustedHosts]);
}
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
return function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};
......@@ -3,52 +3,36 @@
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
private const CONFIG_EXTS = '.{php,xml,yaml,yml}';
public function registerBundles(): iterable
protected function configureContainer(ContainerConfigurator $container): void
{
$contents = require $this->getProjectDir().'/config/bundles.php';
foreach ($contents as $class => $envs) {
if ($envs[$this->environment] ?? $envs['all'] ?? false) {
yield new $class();
}
$container->import('../config/{packages}/*.yaml');
$container->import('../config/{packages}/'.$this->environment.'/*.yaml');
if (is_file(\dirname(__DIR__).'/config/services.yaml')) {
$container->import('../config/services.yaml');
$container->import('../config/{services}_'.$this->environment.'.yaml');
} else {
$container->import('../config/{services}.php');
}
}
public function getProjectDir(): string
protected function configureRoutes(RoutingConfigurator $routes): void
{
return \dirname(__DIR__);
}
$routes->import('../config/{routes}/'.$this->environment.'/*.yaml');
$routes->import('../config/{routes}/*.yaml');
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
{
$container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php'));
$container->setParameter('container.dumper.inline_class_loader', \PHP_VERSION_ID < 70400 || $this->debug);
$container->setParameter('container.dumper.inline_factories', true);
$confDir = $this->getProjectDir().'/config';
$loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{packages}/'.$this->environment.'/*'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob');
}
protected function configureRoutes(RouteCollectionBuilder $routes): void
{
$confDir = $this->getProjectDir().'/config';
$routes->import($confDir.'/{routes}/'.$this->environment.'/*'.self::CONFIG_EXTS, '/', 'glob');
$routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob');
$routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob');
if (is_file(\dirname(__DIR__).'/config/routes.yaml')) {
$routes->import('../config/routes.yaml');
} else {
$routes->import('../config/{routes}.php');
}
}
}
此差异已折叠。
......@@ -3,10 +3,17 @@
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
{# Run `composer require symfony/webpack-encore-bundle`
and uncomment the following Encore helpers to start using Symfony UX #}
{% block stylesheets %}
{#{{ encore_entry_link_tags('app') }}#}
{% endblock %}
{% block javascripts %}
{#{{ encore_entry_script_tags('app') }}#}
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
<?php
use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__).'/vendor/autoload.php';
if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) {
require dirname(__DIR__).'/config/bootstrap.php';
} elseif (method_exists(Dotenv::class, 'bootEnv')) {
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册