Commit 5b2dcf44bcbc4104982dafd29e39da7190924e3b

Authored by Андрей Ларионов
1 parent 486a3601d8

Редактор страниц и SEO, CKEditorContoller, CompanyController

Showing 18 changed files with 788 additions and 22 deletions Side-by-side Diff

app/Classes/Meta.php
... ... @@ -0,0 +1,260 @@
  1 +<?php
  2 +
  3 +
  4 +namespace App\Classes;
  5 +
  6 +
  7 +class Meta
  8 +{
  9 + private static $STATUS = array(
  10 + "initialized"=>0,
  11 + "success"=>1,
  12 + "fail"=>2
  13 + );
  14 + private $metaData;
  15 + private $url;
  16 + private $html;
  17 + private $status;
  18 + private $meta;
  19 +
  20 + /**
  21 + * __construct
  22 + *
  23 + * Require the url of the webpage for meta data extraction
  24 + *
  25 + * @access public
  26 + * @param string $url
  27 + */
  28 + public function __construct($url) {
  29 + $this->url = $url;
  30 + $this->metaData = new \StdClass;
  31 + $this->metaData->title = "";
  32 + $this->metaData->description = "";
  33 + $this->metaData->keywords = "";
  34 + $this->metaData->image = "";
  35 + $this->initialized();
  36 + }
  37 +
  38 + /**
  39 + * parse
  40 + *
  41 + * Parse the meta data from the given url and populate data member $metaData
  42 + *
  43 + * @access public
  44 + * @return integer
  45 + */
  46 + public function parse() {
  47 + // load HTML as DOMDocument object for parsing
  48 + $this->html = new \DOMDocument;
  49 + libxml_use_internal_errors(true);
  50 + $this->html->loadHTML(file_get_contents($this->url));
  51 +
  52 + // php built-in get_meta_tags() only read those with name "title", "description" and so on
  53 + // so I wrote my own version supporting twitter:title, og:title, etc.
  54 + $this->meta = $this->my_get_meta_tags($this->url);
  55 +
  56 + $this->success(); // assume successful
  57 +
  58 + // possible to add more method such as getAuthor()
  59 + $this->getTitle();
  60 + $this->getDescription();
  61 + $this->getKeywords();
  62 + $this->getImage();
  63 +
  64 + return $this->status;
  65 + }
  66 +
  67 +
  68 + /**
  69 + * finalize
  70 + *
  71 + * Export the meta data parsed
  72 + *
  73 + * @access public
  74 + * @return StdClass
  75 + */
  76 + public function finalize() {
  77 + $tmp = new \StdClass;
  78 + $tmp->url = $this->url;
  79 + $tmp->title = $this->metaData->title;
  80 + $tmp->description = $this->metaData->description;
  81 + $tmp->image = $this->metaData->image;
  82 + $tmp->status = $this->status;
  83 + $tmp->keywords = $this->metaData->keywords;
  84 + return $tmp;
  85 + }
  86 +
  87 + /**
  88 + * my_get_meta_tags
  89 + *
  90 + * Require the url to be parsed, read every meta tags found
  91 + *
  92 + * @access private
  93 + * @param string $url
  94 + * @return array
  95 + */
  96 + private function my_get_meta_tags($url) {
  97 + $metatags = $this->html->getElementsByTagName("meta");
  98 + $tmeta = array();
  99 + for ($i=0; $i<$metatags->length; ++$i) {
  100 + $item = $metatags->item($i);
  101 + $name = $item->getAttribute('name');
  102 +
  103 + if (empty($name)) {
  104 + // og meta tags, or twitter meta tags
  105 + $tmeta[$item->getAttribute('property')] = $item->getAttribute('content');
  106 + }
  107 + else {
  108 + // conventional meta tags
  109 + $tmeta[$name] = $item->getAttribute('content');
  110 + }
  111 + }
  112 + return $tmeta;
  113 + }
  114 +
  115 + /**
  116 + * initizlized
  117 + *
  118 + * Set the state of the object to be initizlied
  119 + *
  120 + * @access private
  121 + */
  122 + private function initialized() {
  123 + $this->status = self::$STATUS["initialized"];
  124 + }
  125 +
  126 + /**
  127 + * success
  128 + *
  129 + * Set the state of the object to be successful
  130 + *
  131 + * @access private
  132 + */
  133 + private function success() {
  134 + $this->status = self::$STATUS["success"];
  135 + }
  136 +
  137 + /**
  138 + * fail
  139 + *
  140 + * Set the state of the object to be failed
  141 + *
  142 + * @access private
  143 + */
  144 + private function fail() {
  145 + $this->status = self::$STATUS["fail"];
  146 + }
  147 +
  148 + /**
  149 + * getTitle
  150 + *
  151 + * Read meta title based on priorities of the tag name/property,
  152 + * fallback to reading <title> and <h1> if meta title not present
  153 + *
  154 + * @access private
  155 + */
  156 + private function getTitle() {
  157 + if (isset($this->meta["og:title"])) {
  158 + $this->metaData->title = $this->meta["og:title"];
  159 + return;
  160 + }
  161 +
  162 + if (isset($this->meta["twitter:title"])) {
  163 + $this->metaData->title = $this->meta["twitter:title"];
  164 + return;
  165 + }
  166 +
  167 + if (isset($this->meta["title"])) {
  168 + $this->metaData->title = $this->meta["title"];
  169 + return;
  170 + }
  171 +
  172 + $title = $this->html->getElementsByTagName("title") or $title = $this->html->getElementsByTagName("h1");
  173 + // taking either the title or h1 tag
  174 + if (!$title->length) {
  175 + // if no h1 tag, nothing good enough to be the site title
  176 + $this->fail();
  177 + return;
  178 + }
  179 + else {
  180 + $this->metaData->title = ($title->length) ? $title->item(0)->nodeValue : "";
  181 + }
  182 + }
  183 +
  184 + /**
  185 + * getDescription
  186 + *
  187 + * Read meta description based on priorities of the tag name/property.
  188 + * No fallback, it doesn't read anything except for the meta tag
  189 + *
  190 + * @access private
  191 + */
  192 + private function getDescription() {
  193 + if (isset($this->meta["og:description"])) {
  194 + $this->metaData->description = $this->meta["og:description"];
  195 + return;
  196 + }
  197 +
  198 + if (isset($this->meta["twitter:description"])) {
  199 + $this->metaData->description = $this->meta["twitter:description"];
  200 + return;
  201 + }
  202 +
  203 + if (isset($this->meta["description"])) {
  204 + $this->metaData->description = $this->meta["description"];
  205 + return;
  206 + }
  207 +
  208 + $this->fail();
  209 + return;
  210 + }
  211 +
  212 +
  213 + private function getKeywords() {
  214 + if (isset($this->meta["og:keywords"])) {
  215 + $this->metaData->keywords = $this->meta["og:keywords"];
  216 + return;
  217 + }
  218 +
  219 + if (isset($this->meta["twitter:keywords"])) {
  220 + $this->metaData->keywords = $this->meta["twitter:description"];
  221 + return;
  222 + }
  223 +
  224 + if (isset($this->meta["keywords"])) {
  225 + $this->metaData->keywords = $this->meta["keywords"];
  226 + return;
  227 + }
  228 +
  229 + $this->fail();
  230 + return;
  231 + }
  232 +
  233 +
  234 + /**
  235 + * getImage
  236 + *
  237 + * Read meta image url based on priorities of the tag name/property.
  238 + * No fallback, it doesn't read anything except for the meta tag
  239 + *
  240 + * @access private
  241 + */
  242 + private function getImage() {
  243 + if (isset($this->meta["og:image"])) {
  244 + $this->metaData->image = $this->meta["og:image"];
  245 + return;
  246 + }
  247 +
  248 + if (isset($this->meta["twitter:image"])) {
  249 + $this->metaData->image = $this->meta["twitter:image"];
  250 + return;
  251 + }
  252 +
  253 + if (isset($this->meta["image"])) {
  254 + $this->metaData->image = $this->meta["image"];
  255 + return;
  256 + }
  257 +
  258 + $this->fail();
  259 + }
  260 +}
app/Http/Controllers/Admin/CompanyController.php
... ... @@ -3,11 +3,16 @@
3 3 namespace App\Http\Controllers\Admin;
4 4  
5 5 use App\Http\Controllers\Controller;
  6 +use App\Http\Requests\PagesRequest;
  7 +use App\Http\Requests\SEORequest;
6 8 use App\Models\Employer;
7 9 use App\Models\employers_main;
  10 +use App\Models\header_footer;
8 11 use App\Models\Job_title;
9 12 use App\Models\job_titles_main;
10 13 use App\Models\pages;
  14 +use App\Models\reclame;
  15 +use App\Models\SEO;
11 16 use Illuminate\Http\Request;
12 17  
13 18 class CompanyController extends Controller
... ... @@ -17,11 +22,6 @@ class CompanyController extends Controller
17 22 return;
18 23 }
19 24  
20   - // кабинет - редактор шапки-футера сайта
21   - public function editblocks() {
22   - return;
23   - }
24   -
25 25 // кабинет - редактор должности на главной
26 26 public function job_titles_main(Request $request) {
27 27 if ($request->ajax()) {
... ... @@ -40,6 +40,12 @@ class CompanyController extends Controller
40 40 }
41 41 }
42 42  
  43 + // кабинет - редактор шапки-футера сайта
  44 + public function editblocks() {
  45 + $header_footer = header_footer::query()->OrderBy('name')->paginate(15);
  46 + return view('admin.editbloks.index', compact('header_footer'));
  47 + }
  48 +
43 49 // кабинет - редактор работодатели на главной
44 50 public function employers_main(Request $request) {
45 51 if ($request->ajax()) {
... ... @@ -58,11 +64,36 @@ class CompanyController extends Controller
58 64 }
59 65 }
60 66  
61   - // кабинет - редактор seo-сайта
  67 + //////////// кабинет - редактор seo-сайта /////////////////////////////
62 68 public function editor_seo() {
63   - return;
  69 + $pages = SEO::query()->OrderBy('url')->paginate(15);
  70 + return view('admin.seo.index', compact('pages'));
64 71 }
65 72  
  73 + public function editor_seo_add() {
  74 + return view('admin.seo.add');
  75 + }
  76 +
  77 + public function editor_seo_store(SEORequest $request) {
  78 + SEO::create($request->all());
  79 + return redirect()->route('admin.editor-seo');
  80 + }
  81 +
  82 + public function editor_seo_edit(SEO $page) {
  83 + return view('admin.seo.edit', compact('page'));
  84 + }
  85 +
  86 + public function editor_seo_update(SEORequest $request, SEO $page) {
  87 + $page->update($request->all());
  88 + return redirect()->route('admin.editor-seo');
  89 + }
  90 +
  91 + public function editor_seo_destroy(SEO $page) {
  92 + $page->delete();
  93 + return redirect()->route('admin.editor-seo');
  94 + }
  95 + ///////////////////////////////////////////////////////////////////////
  96 +
66 97 /////////// кабинет - редактор страниц ////////////////////////////////
67 98 public function editor_pages() {
68 99 $pages = pages::query()->OrderBy('name')->paginate(15);
... ... @@ -73,25 +104,29 @@ class CompanyController extends Controller
73 104 return view('admin.pages.add');
74 105 }
75 106  
76   - public function editor_pages_store(Request $request) {
77   - return;
  107 + public function editor_pages_store(PagesRequest $request) {
  108 + pages::create($request->all());
  109 + return redirect()->route('admin.editor-pages');
78 110 }
79 111  
80 112 public function editor_pages_edit(pages $page) {
81 113 return view('admin.pages.edit', compact('page'));
82 114 }
83 115  
84   - public function editor_pages_update(Request $request, pages $page) {
85   - return;
  116 + public function editor_pages_update(PagesRequest $request, pages $page) {
  117 + $page->update($request->all());
  118 + return redirect()->route('admin.editor-pages');
86 119 }
87 120  
88 121 public function editor_pages_destroy(pages $page) {
89   - return;
  122 + $page->delete();
  123 + return redirect()->route('admin.editor-pages');
90 124 }
91 125 ///////////////////////////////////////////////////////////////////
92 126  
93 127 // кабинет - реклама сайта
94 128 public function reclames() {
95   - return;
  129 + $reclames = reclame::query()->OrderBy('title')->paginate(15);
  130 + return view('admin.reclames.index', compact('reclames'));
96 131 }
97 132 }
app/Http/Controllers/CKEditorController.php
... ... @@ -0,0 +1,26 @@
  1 +<?php
  2 +
  3 +namespace App\Http\Controllers;
  4 +
  5 +use Illuminate\Http\Request;
  6 +
  7 +class CKEditorController extends Controller
  8 +{
  9 + public function upload(Request $request)
  10 + {
  11 + if($request->hasFile('upload')) {
  12 + $originName = $request->file('upload')->getClientOriginalName();
  13 + $fileName = pathinfo($originName, PATHINFO_FILENAME);
  14 + $extension = $request->file('upload')->getClientOriginalExtension();
  15 + $fileName = $fileName.'_'.time().'.'.$extension;
  16 + $request->file('upload')->move(public_path('images'), $fileName);
  17 + $CKEditorFuncNum = $request->input('CKEditorFuncNum');
  18 + $url = asset('images/'.$fileName);
  19 + $msg = 'Image successfully uploaded';
  20 + $response = "<script>window.parent.CKEDITOR.tools.callFunction($CKEditorFuncNum, '$url', '$msg')</script>";
  21 +
  22 + @header('Content-type: text/html; charset=utf-8');
  23 + echo $response;
  24 + }
  25 + }
  26 +}
app/Http/Controllers/PagesController.php
... ... @@ -2,11 +2,13 @@
2 2  
3 3 namespace App\Http\Controllers;
4 4  
  5 +use App\Models\pages;
5 6 use Illuminate\Http\Request;
6 7  
7 8 class PagesController extends Controller
8 9 {
9   - public function pages(string $slug) {
10   - return;
  10 + public function pages(pages $pages) {
  11 + $page = pages::query()->where('slug', $pages->slug)->first();
  12 + print_r($page);
11 13 }
12 14 }
app/Http/Requests/PagesRequest.php
... ... @@ -13,7 +13,7 @@ class PagesRequest extends FormRequest
13 13 */
14 14 public function authorize()
15 15 {
16   - return false;
  16 + return true;
17 17 }
18 18  
19 19 /**
... ... @@ -23,8 +23,67 @@ class PagesRequest extends FormRequest
23 23 */
24 24 public function rules()
25 25 {
  26 + $unique = 'unique:pages,slug';
  27 + if (in_array($this->route()->getName(), ['admin.update-page'])) {
  28 + // получаем модель Pages через маршрут admin/editor-pages/edit/{page}
  29 + $model = $this->route('page');
  30 + /*
  31 + * Проверка на уникальность slug, исключая этот пост по идентификатору:
  32 + * 1. posts - таблица базы данных, где проверяется уникальность
  33 + * 2. slug - имя колонки, уникальность значения которой проверяется
  34 + * 3. значение по которому из проверки исключается запись таблицы БД
  35 + * 4. поле, по которому из проверки исключается запись таблицы БД
  36 + * Для проверки будет использован такой SQL-запрос к базе данных:
  37 + * SELECT COUNT(*) FROM `pages` WHERE `slug` = '...' AND `id` <> 17
  38 + */
  39 + $unique = 'unique:pages,slug,'.$model->id.',id';
  40 + }
  41 +
26 42 return [
27   - //
  43 + 'name' => [
  44 + 'required',
  45 + 'string',
  46 + 'min:3',
  47 + 'max:255',
  48 + ],
  49 + 'slug' => [
  50 + 'required',
  51 + 'max:255',
  52 + $unique,
  53 + 'regex:~^[-_a-z0-9]+$~i',
  54 + ],
  55 + 'anons' => [
  56 + 'required',
  57 + 'min:500',
  58 + ],
  59 + 'text' => [
  60 + 'required',
  61 + 'min:500',
  62 + ],
  63 + 'image' => [
  64 + 'mimes:jpeg,jpg,png',
  65 + 'max:15000'
  66 + ],
  67 + ];
  68 + }
  69 +
  70 + public function messages() {
  71 + return [
  72 + 'required' => 'Поле :attribute обязательно для ввода',
  73 + 'unique' => 'Поле :attribute должно быть уникальным',
  74 + 'mimes' => 'Допускаются файлы только с расширением jpeg,jpg,png',
  75 + 'min' => [
  76 + 'string' => 'Поле «:attribute» должно быть не меньше :min символов',
  77 + 'integer' => 'Поле «:attribute» должно быть :min или больше',
  78 + 'file' => 'Файл «:attribute» должен быть не меньше :min Кбайт'
  79 + ],
  80 +
  81 + 'max' => [
  82 + 'string' => 'Поле «:attribute» должно быть не больше :max символов',
  83 + 'integer' => 'Поле «:attribute» должно быть :max или меньше',
  84 + 'file' => 'Файл «:attribute» должен быть не больше :max Кбайт'
  85 + ],
  86 +
28 87 ];
29 88 }
30 89 }
app/Http/Requests/SEORequest.php
... ... @@ -0,0 +1,30 @@
  1 +<?php
  2 +
  3 +namespace App\Http\Requests;
  4 +
  5 +use Illuminate\Foundation\Http\FormRequest;
  6 +
  7 +class SEORequest extends FormRequest
  8 +{
  9 + /**
  10 + * Determine if the user is authorized to make this request.
  11 + *
  12 + * @return bool
  13 + */
  14 + public function authorize()
  15 + {
  16 + return false;
  17 + }
  18 +
  19 + /**
  20 + * Get the validation rules that apply to the request.
  21 + *
  22 + * @return array<string, mixed>
  23 + */
  24 + public function rules()
  25 + {
  26 + return [
  27 + //
  28 + ];
  29 + }
  30 +}
app/Models/pages.php
... ... @@ -8,4 +8,13 @@ use Illuminate\Database\Eloquent\Model;
8 8 class pages extends Model
9 9 {
10 10 use HasFactory;
  11 +
  12 + protected $fillable = [
  13 + 'name',
  14 + 'slug',
  15 + 'text',
  16 + 'anons',
  17 + 'author',
  18 + 'image',
  19 + ];
11 20 }
resources/views/admin/editbloks/index.blade.php
... ... @@ -0,0 +1,95 @@
  1 +@extends('layout.admin', ['title' => 'Админка - Редактор шапки-футера сайта'])
  2 +
  3 +@section('script')
  4 +
  5 +@endsection
  6 +
  7 +@section('search')
  8 + <!--<div class="absolute inset-y-0 flex items-center pl-2">
  9 + <svg
  10 + class="w-4 h-4"
  11 + aria-hidden="true"
  12 + fill="currentColor"
  13 + viewBox="0 0 20 20"
  14 + >
  15 + <path
  16 + fill-rule="evenodd"
  17 + d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
  18 + clip-rule="evenodd"
  19 + ></path>
  20 + </svg>
  21 + </div>
  22 + <form action="" method="POST">
  23 + <div style="float:left;"><input
  24 + class="w-full pl-8 pr-2 text-sm text-gray-700 placeholder-gray-600 bg-gray-100 border-0 rounded-md dark:placeholder-gray-500 dark:focus:shadow-outline-gray dark:focus:placeholder-gray-600 dark:bg-gray-700 dark:text-gray-200 focus:placeholder-gray-500 focus:bg-white focus:border-purple-300 focus:outline-none focus:shadow-outline-purple form-input"
  25 + style="width: 400px"
  26 + type="text"
  27 + placeholder="Искать..."
  28 + aria-label="Search"
  29 + /></div>
  30 + <div style="float: left">
  31 + <button type="submit" class="px-3 py-1 rounded-md focus:outline-none focus:shadow-outline-purple">Искать</button>
  32 + </div>
  33 + </form>-->
  34 +@endsection
  35 +
  36 +@section('content')
  37 +
  38 + <a href="{{ route('admin.add-seo') }}" style="width: 145px" class="px-3 py-1 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-md active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple">
  39 + Добавить опцию
  40 + </a>
  41 + <br>
  42 + <div class="w-full overflow-hidden rounded-lg shadow-xs" id="ajax_block">
  43 +
  44 + <div class="w-full overflow-x-auto">
  45 + <table class="w-full whitespace-no-wrap">
  46 + <thead>
  47 + <tr
  48 + class="text-xs font-semibold tracking-wide text-left text-gray-500 uppercase border-b dark:border-gray-700 bg-gray-50 dark:text-gray-400 dark:bg-gray-800"
  49 + >
  50 + <th class="px-4 py-3">№</th>
  51 + <th class="px-4 py-3">Название</th>
  52 + <th class="px-4 py-3">Ссылка</th>
  53 + <th class="px-4 py-3">Категория</th>
  54 + <th class="px-4 py-3">Шапка</th>
  55 + <th class="px-4 py-3">Дата создания</th>
  56 + <th class="px-4 py-3">Редактировать</th>
  57 + </tr>
  58 + </thead>
  59 + <tbody class="bg-white divide-y dark:divide-gray-700 dark:bg-gray-800">
  60 + @foreach($header_footer as $page)
  61 + <tr class="text-gray-700 dark:text-gray-400">
  62 + <td class="px-4 py-3">
  63 + {{$page->id}}
  64 + </td>
  65 + <td class="px-4 py-3">
  66 + {{$page->name}}
  67 + </td>
  68 + <td class="px-4 py-3">
  69 + {{$page->link}}
  70 + </td>
  71 + <td class="px-4 py-3">
  72 + {{$page->category}} ({{$page->code_id}})
  73 + </td>
  74 + <td class="px-4 py-3">
  75 + {{$page->created_at}}
  76 + </td>
  77 + <td class="px-4 py-3 text-sm_">
  78 + <form action="{{ route('admin.delete-seo', ['page' => $page->id]) }}" method="POST">
  79 + <a href="{{ route('admin.edit-seo', ['page' => $page->id]) }}">Изменить</a> |
  80 + @csrf
  81 + @method('DELETE')
  82 + <input class="btn btn-danger" type="submit" value="Удалить"/>
  83 + </form>
  84 + </td>
  85 + </tr>
  86 + @endforeach
  87 + </tbody>
  88 + </table>
  89 + </div>
  90 +
  91 + <div class="grid px-4 py-3 text-xs font-semibold tracking-wide text-gray-500 uppercase border-t dark:border-gray-700 bg-gray-50 sm:grid-cols-9 dark:text-gray-400 dark:bg-gray-800">
  92 + <?=$header_footer->appends($_GET)->links('admin.pagginate'); ?>
  93 + </div>
  94 + </div>
  95 +@endsection
resources/views/admin/pages/add.blade.php
1 1 @extends('layout.admin', ['title' => 'Админка - Добавление страницы'])
2 2  
3 3 @section('content')
4   - <form method="POST" action="{{ route('admin.add-page-store') }}">
  4 + <form method="POST" action="{{ route('admin.add-page-store') }}" enctype="multipart/form-data">
5 5 @include('admin.pages.form')
6 6 </form>
7 7 @endsection
resources/views/admin/pages/edit.blade.php
1 1 @extends('layout.admin', ['title' => 'Админка - Редактирование страницы'])
2 2  
3 3 @section('content')
4   - <form method="POST" action="{{ route('admin.update-page', ['page' => $page->id]) }}">
  4 + <form method="POST" action="{{ route('admin.update-page', ['page' => $page->id]) }}" enctype="multipart/form-data">
5 5 @include('admin.pages.form')
6 6 </form>
7 7 @endsection
resources/views/admin/pages/form.blade.php
... ... @@ -4,6 +4,56 @@
4 4 @method('PUT')
5 5 @endisset
6 6  
  7 +<script src="//cdn.ckeditor.com/4.14.0/standard/ckeditor.js"></script>
  8 +<script>
  9 + CKEDITOR.replace( 'anons');
  10 + CKEDITOR.replace( 'text', {
  11 + filebrowserUploadUrl: "{{route('ckeditor.image-upload', ['_token' => csrf_token() ])}}",
  12 + filebrowserUploadMethod: 'form'
  13 + });
  14 +</script>
  15 +<script>
  16 + function translit(word){
  17 + var answer = '';
  18 + var converter = {
  19 + 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd',
  20 + 'е': 'e', 'ё': 'e', 'ж': 'zh', 'з': 'z', 'и': 'i',
  21 + 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n',
  22 + 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't',
  23 + 'у': 'u', 'ф': 'f', 'х': 'h', 'ц': 'c', 'ч': 'ch',
  24 + 'ш': 'sh', 'щ': 'sch', 'ь': '', 'ы': 'y', 'ъ': '',
  25 + 'э': 'e', 'ю': 'yu', 'я': 'ya',
  26 +
  27 + 'А': 'A', 'Б': 'B', 'В': 'V', 'Г': 'G', 'Д': 'D',
  28 + 'Е': 'E', 'Ё': 'E', 'Ж': 'Zh', 'З': 'Z', 'И': 'I',
  29 + 'Й': 'Y', 'К': 'K', 'Л': 'L', 'М': 'M', 'Н': 'N',
  30 + 'О': 'O', 'П': 'P', 'Р': 'R', 'С': 'S', 'Т': 'T',
  31 + 'У': 'U', 'Ф': 'F', 'Х': 'H', 'Ц': 'C', 'Ч': 'Ch',
  32 + 'Ш': 'Sh', 'Щ': 'Sch', 'Ь': '', 'Ы': 'Y', 'Ъ': '',
  33 + 'Э': 'E', 'Ю': 'Yu', 'Я': 'Ya', ' ': '-'
  34 + };
  35 +
  36 + for (var i = 0; i < word.length; ++i ) {
  37 + if (converter[word[i]] == undefined){
  38 + answer += word[i];
  39 + } else {
  40 + answer += converter[word[i]];
  41 + }
  42 + }
  43 +
  44 + return answer;
  45 + }
  46 +
  47 + window.addEventListener("DOMContentLoaded", (event) => {
  48 + let title = document.querySelector('#name');
  49 + let text = document.querySelector('#slug');
  50 +
  51 + title.addEventListener('input', function() {
  52 + text.value = translit(this.value);
  53 + });
  54 + });
  55 +
  56 +</script>
7 57 <div class="px-4 py-3 mb-8 bg-white rounded-lg shadow-md dark:bg-gray-800">
8 58  
9 59 <label class="px-4 py-3 mb-8 bg-white rounded-lg shadow-md dark:bg-gray-800">
... ... @@ -69,8 +119,9 @@
69 119 </label><br>
70 120  
71 121 <label class="block text-sm">
72   - <input type="file" class="form-control-file" name="image" accept="image/png, image/jpeg">
73   - </label>
  122 + <span class="text-gray-700 dark:text-gray-400">Картинка</span>
  123 + <input type="file" class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input" id="image" name="image" accept="image/png, image/jpeg">
  124 + </label><br>
74 125  
75 126 <div class="flex flex-col flex-wrap mb-4 space-y-4 md:flex-row md:items-end md:space-x-4">
76 127 <div>
resources/views/admin/pages/index.blade.php
... ... @@ -64,7 +64,7 @@
64 64 {{$page->name}}
65 65 </td>
66 66 <td class="px-4 py-3">
67   - {{ action([\App\Http\Controllers\PagesController::class, 'pages'], ['slug' => $page->slug]) }}
  67 + <a target="blank" href="{{ route('page', ['pages' => $page]) }}">{{ route('page', ['pages' => $page]) }}</a>
68 68 </td>
69 69 <td class="px-4 py-3">
70 70 {{$page->created_at}}
resources/views/admin/reclames/index.blade.php
... ... @@ -0,0 +1,98 @@
  1 +@extends('layout.admin', ['title' => 'Админка - Реклама сайта'])
  2 +
  3 +@section('script')
  4 +
  5 +@endsection
  6 +
  7 +@section('search')
  8 + <!--<div class="absolute inset-y-0 flex items-center pl-2">
  9 + <svg
  10 + class="w-4 h-4"
  11 + aria-hidden="true"
  12 + fill="currentColor"
  13 + viewBox="0 0 20 20"
  14 + >
  15 + <path
  16 + fill-rule="evenodd"
  17 + d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
  18 + clip-rule="evenodd"
  19 + ></path>
  20 + </svg>
  21 + </div>
  22 + <form action="" method="POST">
  23 + <div style="float:left;"><input
  24 + class="w-full pl-8 pr-2 text-sm text-gray-700 placeholder-gray-600 bg-gray-100 border-0 rounded-md dark:placeholder-gray-500 dark:focus:shadow-outline-gray dark:focus:placeholder-gray-600 dark:bg-gray-700 dark:text-gray-200 focus:placeholder-gray-500 focus:bg-white focus:border-purple-300 focus:outline-none focus:shadow-outline-purple form-input"
  25 + style="width: 400px"
  26 + type="text"
  27 + placeholder="Искать..."
  28 + aria-label="Search"
  29 + /></div>
  30 + <div style="float: left">
  31 + <button type="submit" class="px-3 py-1 rounded-md focus:outline-none focus:shadow-outline-purple">Искать</button>
  32 + </div>
  33 + </form>-->
  34 +@endsection
  35 +
  36 +@section('content')
  37 +
  38 + <a href="{{ route('admin.add-seo') }}" style="width: 160px" class="px-3 py-1 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-md active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple">
  39 + Добавить рекламу
  40 + </a>
  41 + <br>
  42 + <div class="w-full overflow-hidden rounded-lg shadow-xs" id="ajax_block">
  43 +
  44 + <div class="w-full overflow-x-auto">
  45 + <table class="w-full whitespace-no-wrap">
  46 + <thead>
  47 + <tr
  48 + class="text-xs font-semibold tracking-wide text-left text-gray-500 uppercase border-b dark:border-gray-700 bg-gray-50 dark:text-gray-400 dark:bg-gray-800"
  49 + >
  50 + <th class="px-4 py-3">№</th>
  51 + <th class="px-4 py-3">Заголовок</th>
  52 + <th class="px-4 py-3">Ссылка</th>
  53 + <th class="px-4 py-3">Позиция</th>
  54 + <th class="px-4 py-3">Скрыть</th>
  55 + <th class="px-4 py-3">Клики</th>
  56 + <th class="px-4 py-3">Редактировать</th>
  57 + </tr>
  58 + </thead>
  59 + <tbody class="bg-white divide-y dark:divide-gray-700 dark:bg-gray-800">
  60 + @foreach($reclames as $reclame)
  61 + <tr class="text-gray-700 dark:text-gray-400">
  62 + <td class="px-4 py-3">
  63 + {{$reclame->id}}
  64 + </td>
  65 + <td class="px-4 py-3">
  66 + {{$reclame->title}}
  67 + </td>
  68 + <td class="px-4 py-3">
  69 + {{$reclame->link}}
  70 + </td>
  71 + <td class="px-4 py-3">
  72 + {{$reclame->position}}
  73 + </td>
  74 + <td class="px-4 py-3">
  75 + {{$reclame->is_hidden}}
  76 + </td>
  77 + <td class="px-4 py-3">
  78 + {{$reclame->col_vo_click}}
  79 + </td>
  80 + <td class="px-4 py-3 text-sm_">
  81 + <form action="{{ route('admin.delete-seo', ['page' => $reclame->id]) }}" method="POST">
  82 + <a href="{{ route('admin.edit-seo', ['page' => $reclame->id]) }}">Изменить</a> |
  83 + @csrf
  84 + @method('DELETE')
  85 + <input class="btn btn-danger" type="submit" value="Удалить"/>
  86 + </form>
  87 + </td>
  88 + </tr>
  89 + @endforeach
  90 + </tbody>
  91 + </table>
  92 + </div>
  93 +
  94 + <div class="grid px-4 py-3 text-xs font-semibold tracking-wide text-gray-500 uppercase border-t dark:border-gray-700 bg-gray-50 sm:grid-cols-9 dark:text-gray-400 dark:bg-gray-800">
  95 + <?=$reclames->appends($_GET)->links('admin.pagginate'); ?>
  96 + </div>
  97 + </div>
  98 +@endsection
resources/views/admin/seo/add.blade.php
resources/views/admin/seo/edit.blade.php
resources/views/admin/seo/form.blade.php
resources/views/admin/seo/index.blade.php
... ... @@ -0,0 +1,90 @@
  1 +@extends('layout.admin', ['title' => 'Админка - Страницы SEO сайта'])
  2 +
  3 +@section('script')
  4 +
  5 +@endsection
  6 +
  7 +@section('search')
  8 + <!--<div class="absolute inset-y-0 flex items-center pl-2">
  9 + <svg
  10 + class="w-4 h-4"
  11 + aria-hidden="true"
  12 + fill="currentColor"
  13 + viewBox="0 0 20 20"
  14 + >
  15 + <path
  16 + fill-rule="evenodd"
  17 + d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
  18 + clip-rule="evenodd"
  19 + ></path>
  20 + </svg>
  21 + </div>
  22 + <form action="" method="POST">
  23 + <div style="float:left;"><input
  24 + class="w-full pl-8 pr-2 text-sm text-gray-700 placeholder-gray-600 bg-gray-100 border-0 rounded-md dark:placeholder-gray-500 dark:focus:shadow-outline-gray dark:focus:placeholder-gray-600 dark:bg-gray-700 dark:text-gray-200 focus:placeholder-gray-500 focus:bg-white focus:border-purple-300 focus:outline-none focus:shadow-outline-purple form-input"
  25 + style="width: 400px"
  26 + type="text"
  27 + placeholder="Искать..."
  28 + aria-label="Search"
  29 + /></div>
  30 + <div style="float: left">
  31 + <button type="submit" class="px-3 py-1 rounded-md focus:outline-none focus:shadow-outline-purple">Искать</button>
  32 + </div>
  33 + </form>-->
  34 +@endsection
  35 +
  36 +@section('content')
  37 +
  38 + <a href="{{ route('admin.add-seo') }}" style="width: 200px" class="px-3 py-1 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-md active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple">
  39 + Добавить seo странице
  40 + </a>
  41 + <br>
  42 + <div class="w-full overflow-hidden rounded-lg shadow-xs" id="ajax_block">
  43 +
  44 + <div class="w-full overflow-x-auto">
  45 + <table class="w-full whitespace-no-wrap">
  46 + <thead>
  47 + <tr
  48 + class="text-xs font-semibold tracking-wide text-left text-gray-500 uppercase border-b dark:border-gray-700 bg-gray-50 dark:text-gray-400 dark:bg-gray-800"
  49 + >
  50 + <th class="px-4 py-3">№</th>
  51 + <th class="px-4 py-3">URL страницы</th>
  52 + <th class="px-4 py-3">title страницы</th>
  53 + <th class="px-4 py-3">Дата создания</th>
  54 + <th class="px-4 py-3">Редактировать</th>
  55 + </tr>
  56 + </thead>
  57 + <tbody class="bg-white divide-y dark:divide-gray-700 dark:bg-gray-800">
  58 + @foreach($pages as $page)
  59 + <tr class="text-gray-700 dark:text-gray-400">
  60 + <td class="px-4 py-3">
  61 + {{$page->id}}
  62 + </td>
  63 + <td class="px-4 py-3">
  64 + {{$page->url}}
  65 + </td>
  66 + <td class="px-4 py-3">
  67 + {{$page->title}}
  68 + </td>
  69 + <td class="px-4 py-3">
  70 + {{$page->created_at}}
  71 + </td>
  72 + <td class="px-4 py-3 text-sm_">
  73 + <form action="{{ route('admin.delete-seo', ['page' => $page->id]) }}" method="POST">
  74 + <a href="{{ route('admin.edit-seo', ['page' => $page->id]) }}">Изменить</a> |
  75 + @csrf
  76 + @method('DELETE')
  77 + <input class="btn btn-danger" type="submit" value="Удалить"/>
  78 + </form>
  79 + </td>
  80 + </tr>
  81 + @endforeach
  82 + </tbody>
  83 + </table>
  84 + </div>
  85 +
  86 + <div class="grid px-4 py-3 text-xs font-semibold tracking-wide text-gray-500 uppercase border-t dark:border-gray-700 bg-gray-50 sm:grid-cols-9 dark:text-gray-400 dark:bg-gray-800">
  87 + <?=$pages->appends($_GET)->links('admin.pagginate'); ?>
  88 + </div>
  89 + </div>
  90 +@endsection
... ... @@ -9,6 +9,7 @@ use App\Http\Controllers\Admin\UsersController;
9 9 use App\Http\Controllers\Admin\WorkersController;
10 10 use App\Http\Controllers\Auth\LoginController;
11 11 use App\Http\Controllers\Auth\RegisterController;
  12 +use App\Http\Controllers\CKEditorController;
12 13 use App\Models\User;
13 14 use App\Http\Controllers\MainController;
14 15 use App\Http\Controllers\HomeController;
... ... @@ -17,6 +18,7 @@ use App\Http\Controllers\Admin\CompanyController;
17 18 use App\Http\Controllers\Admin\Ad_EmployersController;
18 19 use App\Http\Controllers\Admin\MsgAnswersController;
19 20 use App\Http\Controllers\Admin\GroupsController;
  21 +use App\Http\Controllers\PagesController;
20 22  
21 23  
22 24 /*
... ... @@ -180,6 +182,11 @@ Route::group([
180 182  
181 183 // кабинет - редактор seo-сайта
182 184 Route::get('editor-seo', [CompanyController::class, 'editor_seo'])->name('editor-seo');
  185 + Route::get('editor-seo/add', [CompanyController::class, 'editor_seo_add'])->name('add-seo');
  186 + Route::post('editor-seo/add', [CompanyController::class, 'editor_seo_store'])->name('add-seo-store');
  187 + Route::get('editor-seo/edit/{page}', [CompanyController::class, 'editor_seo_edit'])->name('edit-seo');
  188 + Route::put('editor-seo/edit/{page}', [CompanyController::class, 'editor_seo_update'])->name('update-seo');
  189 + Route::delete('editor-seo/delete/{page}', [CompanyController::class, 'editor_seo_destroy'])->name('delete-seo');
183 190  
184 191  
185 192 // кабинет - редактор страниц
... ... @@ -225,3 +232,7 @@ Route::group([
225 232 Route::get('roles', [UsersController::class, 'roles'])->name('roles');
226 233  
227 234 });
  235 +
  236 +Route::post('ckeditor/upload', [CKEditorController::class, 'upload'])->name('ckeditor.image-upload');
  237 +
  238 +Route::get('pages/{pages:slug}', [PagesController::class, 'pages'])->name('page');