├── .gitattributes
├── .gitignore
├── .scrutinizer.yml
├── README.md
├── composer.json
└── src
├── Commands
└── GenerateSeoManagerData.php
├── Controllers
├── ImportController.php
├── LocalesController.php
└── ManagerController.php
├── Facades
└── SeoManager.php
├── Middleware
├── ClearViewCache.php
└── SeoManager.php
├── Models
├── Locale.php
├── SeoManager.php
└── Translate.php
├── SeoManager.php
├── SeoManagerServiceProvider.php
├── Traits
├── Appends.php
└── SeoManagerTrait.php
├── assets
├── css
│ └── theme.min.css
├── fonts
│ ├── cerebrisans
│ │ ├── cerebrisans-medium.eot
│ │ ├── cerebrisans-medium.ttf
│ │ ├── cerebrisans-medium.woff
│ │ ├── cerebrisans-mediumd41d.eot
│ │ ├── cerebrisans-regular.eot
│ │ ├── cerebrisans-regular.ttf
│ │ ├── cerebrisans-regular.woff
│ │ ├── cerebrisans-regulard41d.eot
│ │ ├── cerebrisans-semibold.eot
│ │ ├── cerebrisans-semibold.ttf
│ │ ├── cerebrisans-semibold.woff
│ │ └── cerebrisans-semiboldd41d.eot
│ └── feather
│ │ ├── feather.min.css
│ │ └── fonts
│ │ ├── Feather144f.svg
│ │ ├── Feather144f.ttf
│ │ └── Feather144f.woff
├── img
│ ├── logo.png
│ ├── logo.svg
│ ├── nophoto.png
│ └── scale.svg
├── js
│ ├── seo-manager.app.js
│ └── theme.min.js
└── libs
│ ├── bootstrap
│ └── dist
│ │ └── js
│ │ └── bootstrap.bundle.min.js
│ └── jquery
│ └── dist
│ └── jquery.min.js
├── config
└── seo-manager.php
├── helpers
└── helpers.php
├── migrations
├── 2018_10_29_174832_create_lionix_seo_manager_table.php
├── 2019_01_08_132731_create_locales_table.php
└── 2019_01_12_174747_create_translates_table.php
├── routes
└── seo-manager.php
├── views
├── index.blade.php
└── og_data.blade.php
└── vue
├── App.vue
├── components
├── Header.vue
├── SideBar.vue
└── routes
│ ├── Routes.vue
│ └── partials
│ ├── Actions.vue
│ ├── Author.vue
│ ├── Description.vue
│ ├── Keywords.vue
│ ├── Mapping.vue
│ ├── OgData.vue
│ ├── Params.vue
│ ├── Route.vue
│ ├── Title.vue
│ ├── TitleDynamic.vue
│ ├── Url.vue
│ ├── mappings
│ ├── ParamsMapping.vue
│ └── TitleMapping.vue
│ └── modals
│ ├── AuthorModal.vue
│ ├── DescriptionModal.vue
│ ├── KeywordsModal.vue
│ ├── MappingModal.vue
│ ├── OgDataModal.vue
│ ├── PreviewModal.vue
│ ├── TitleDynamicModal.vue
│ ├── TitleModal.vue
│ └── UrlModal.vue
├── event_bus
└── index.js
├── seo-manager.app.js
└── store
└── store.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | src/assets/* linguist-vendored
2 | src/vue/* linguist-vendored
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/.gitignore
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/.scrutinizer.yml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Seo Manager Package for Laravel ( with Localization )
2 |
3 | [](https://madewithlaravel.com/p/seo-manager/shield-link)
4 | [](https://scrutinizer-ci.com/g/lionix-team/seo-manager/)
5 |
6 | `lionix/seo-manager` package will provide
7 | you an interface from where you can manage all your pages
8 | metadata separately and get dynamically changing content.
9 | Let's see how.
10 |
11 | # Update:
12 | After updating to version > v1.2.* you have to run:
13 | ```bash
14 | php artisan vendor:publish --provider="Lionix\SeoManager\SeoManagerServiceProvider" --tag=config --force
15 | ```
16 | ```bash
17 | php artisan vendor:publish --provider="Lionix\SeoManager\SeoManagerServiceProvider" --tag=assets --force
18 | ```
19 | and
20 | ```bash
21 | php artisan migrate
22 | ```
23 | to publish latest configs and new migration files.
24 |
25 | ## Localization
26 | In version v1.2.* you will have availability
27 | to localize your metadata and set translates
28 | to your data.
29 |
30 | For that, you just should
31 | add your available locales
32 | with top right button "Add Locales" and
33 | chose locale for which you want to add translations.
34 |
35 | That's it,
36 | Package will automatically detect your locale
37 | from website and will set needed translations,
38 | you don't need to do any other configs.
39 | Easy, isn't it?
40 |
41 | ## Installation
42 | You can install the package via composer:
43 |
44 | ```bash
45 | composer require lionix/seo-manager
46 | ```
47 | Publishing package files
48 | ```bash
49 | php artisan vendor:publish --provider="Lionix\SeoManager\SeoManagerServiceProvider"
50 | ```
51 | This command will create `config/seo-manager.php`
52 | file and will copy package assets directory to `public/vendor/lionix`.
53 |
54 | #### Configurations
55 |
56 | In `config/seo-manager.php` file you can do the following configurations:
57 |
58 | ```php
59 | return [
60 | /**
61 | * Database table name where your manager data will be stored
62 | */
63 | 'database' => [
64 | 'table' => 'seo_manager',
65 | 'locales_table' => 'seo_manager_locales',
66 | 'translates_table' => 'seo_manager_translates'
67 | ],
68 |
69 | /**
70 | * Set default locale,
71 | * It will be added as default locale
72 | * when locales table migrated
73 | */
74 | 'locale' => 'en',
75 |
76 | /**
77 | * Path where your eloquent models are
78 | */
79 | 'models_path' => '',
80 |
81 | /**
82 | * Route from which your Dashboard will be available
83 | */
84 | 'route' => 'seo-manager',
85 |
86 | /**
87 | * Middleware array for dashboard
88 | * to prevent unauthorized users visit the manager
89 | */
90 | 'middleware' => [
91 | // 'auth',
92 | ],
93 |
94 | /**
95 | * Routes which shouldn't be imported to seo manager
96 | */
97 | 'except_routes' => [
98 | 'seo-manager',
99 | 'admin'
100 | //
101 | ],
102 |
103 | /**
104 | * Columns which shouldn't be used ( in mapping )
105 | */
106 | 'except_columns' => [
107 | // "created_at",
108 | // "updated_at",
109 | ],
110 |
111 | /**
112 | * Set this parameter to true
113 | * if you want to have "$metaData" variable
114 | * shared between all views in "web" middleware group
115 | */
116 | 'shared_meta_data' => false
117 | ];
118 | ```
119 |
120 | After finishing with all configurations run `php artisan migrate` to create SEO manager table.
121 |
122 | That's it, now your SEO manager will be available
123 | from /seo-manager URL (or, if you changed route config,
124 | by that config URL)
125 |
126 | ## Usage
127 |
128 | After visiting your dashboard URL you have to import your routes to start to manage them.
129 |
130 | 
131 |
132 | Route importing will store all your GET routes into the database ( except the ones which specified in "except_routes" configs).
133 |
134 | 
135 |
136 | ### Let the fun begin!
137 |
138 | **Mapping**
139 |
140 | 
141 |
142 | To get dynamically changing metadata you should map your route params to the correct Models.
143 |
144 | *Param: Route param*
145 |
146 | *Model: Eloquent Model which you are using to get the record by route param*
147 |
148 | *Find By: Database table column name which you are using to get the record by route param*
149 |
150 | *Use Columns: Database table columns which we should use for further mapping*
151 |
152 |
153 | > **Note**: If you have appended attributes inside your model via `protected $appends` and you want to use them in mapping
154 | you have to use `Lionix\SeoManager\Traits\Appends;` trait inside your model.
155 |
156 | *Mapping available only if your route has params*
157 |
158 | Next steps you can do, is to set Keywords, Description, Title, URL, Author, Title Dynamic, OpenGraph Data.
159 |
160 | **About "Title Dynamic":**
161 |
162 | Here you can drag & drop your mapped params, your title and write custom text to generate the dynamic title for your page.
163 | Every time your "title" will be changed or your mapped params value changed, the dynamic title will be changed automatically.
164 |
165 | 
166 |
167 | **About "Open Graph":**
168 |
169 | Here you can write your open graph data or map your fields to mapping based on params.
170 |
171 | 
172 |
173 | ## Example Usage
174 |
175 | ## Via `SeoManager` Facade
176 |
177 | ```php
178 | use Lionix\SeoManager\Facades\SeoManager;
179 | ```
180 | ##### This will return an array with all your SEO Manager data
181 | ```
182 | SeoManager::metaData();
183 | ```
184 | *Example:*
185 | ```php
186 | array:13 [▼
187 | "keywords" => "First Keyword, Second, Third"
188 | "description" => "Test Description"
189 | "title" => "Test Titile"
190 | "url" => "http://example.com/users/1"
191 | "author" => "Sergey Karakhanyan"
192 | "title_dynamic" => "Test Titile - Custom Text - Test User Name "
193 | "og:url" => "http://example.com/users/1"
194 | "og:type" => "website"
195 | "og:image:url" => "https://wallpaperbrowse.com/media/images/3848765-wallpaper-images-download.jpg"
196 | "og:title" => "Test Titile - Custom Open Graph Text"
197 | "og:locale" => "en_GB"
198 | "og:site_name" => "Seo Manager Package"
199 | "og:description" => "Open Graph Description"
200 | ]
201 | ```
202 |
203 | `SeoManager::metaData();` method can receive property variable
204 | to get the value of some property
205 |
206 | *Example:*
207 |
208 | `SeoManager::metaData('keywords');` will return `"First Keyword, Second, Third"`
209 |
210 |
211 | ##### To get only OpenGraph data array:
212 | ```php
213 | SeoManager::metaData('og_data');
214 | ```
215 | *Example*
216 |
217 | ```php
218 | array:7 [▼
219 | "og:url" => "http://example.com/users/1"
220 | "og:type" => "website"
221 | "og:image:url" => "https://wallpaperbrowse.com/media/images/3848765-wallpaper-images-download.jpg"
222 | "og:title" => "Test Titile - Custom Open Graph Text"
223 | "og:locale" => "en_GB"
224 | "og:site_name" => "Seo Manager Package"
225 | "og:description" => "Open Graph Description"
226 | ]
227 | ```
228 | ##### Aliases
229 | `SeoManager::metaKeywords()`
230 |
231 | `SeoManager::metaTitle()`
232 |
233 | `SeoManager::metaDescription()`
234 |
235 | `SeoManager::metaUrl()`
236 |
237 | `SeoManager::metaAuthor()`
238 |
239 | `SeoManager::metaTitleDynamic()`
240 |
241 | `SeoManager::metaOpenGraph()` - Can receive property variable to get the value of some OG property
242 |
243 | *Example*
244 |
245 | `SeoManager::metaOpenGraph('og:image:url')`
246 |
247 | Will return
248 | `"https://wallpaperbrowse.com/media/images/3848765-wallpaper-images-download.jpg"`
249 |
250 |
251 | ## Via `helper` functions
252 |
253 | `metaData()`
254 |
255 | `metaKeywords()`
256 |
257 | `metaTitle()`
258 |
259 | `metaDescription()`
260 |
261 | `metaUrl()`
262 |
263 | `metaAuthor()`
264 |
265 | `metaTitleDynamic()`
266 |
267 | `metaOpenGraph()`
268 |
269 | ## Via @Blade directives
270 |
271 | You can use this blade directives in your view files to get metadata.
272 |
273 | `@meta`
274 |
275 | *Output Example*
276 |
277 | ```html
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 | ```
292 |
293 | `@meta` can receive property param
294 |
295 | `@meta('description')`
296 |
297 | *Output Example*
298 |
299 | ```html
300 |
301 | ```
302 | >**Note:**
303 | You can't add open graph properties to `@meta()` like `@meta('og:url')`
304 | But you can get only OpenGraph meta data by `@meta('og_data')`.
305 | If you want to get concrete OG param meta tag you can use `@openGraph` (*similar to `@meta('og_data')`*)
306 | and pass param there like `@openGraph('og:url)`
307 |
308 | >**Note #2:** If you want to do modifications in your og data and display it manually, you should do that before `@meta`
309 |
310 | *Example:*
311 | ```html
312 |
313 | @meta
314 | ```
315 | ##### Aliases
316 |
317 | `@keywords`
318 |
319 | `@url`
320 |
321 | `@author`
322 |
323 | `@description`
324 |
325 | `@title`
326 |
327 | `@openGraph`
328 |
329 | `@titleDynamic` - will return dynamically generated title which you can use inside your `
` tags.
330 |
331 | ## Contributing
332 |
333 | ### How to start developing on this project
334 |
335 | 1. Install the module in a existing Laravel project and check if the module is working.
336 |
337 | 2. Delete the folder vendor/lionix/seo-manager.
338 |
339 | 3. Copy the fork of the module in the vendor/lionix/seo-manager folder
340 | (don't know how to make a fork? https://kbroman.org/github_tutorial/pages/fork.html)
341 |
342 | 4. Add the following line to your webpack.mix.js
343 | `mix.js('vendor/lionix/seo-manager/src/vue/seo-manager.app.js', 'public/vendor/lionix/js');`
344 |
345 | 5. run the NPM Watch command `npm run watch`
346 |
347 | 6. If everything works fine, you can start developing on the module
348 |
349 | 7. **PLEASE REMIND TO COPY
350 | YOUR `public/vendor/lionix/js/seo-manager.app.js` TO `vendor/lionix/seo-manager/src/assets/seo-manager.app.js`**
351 |
352 | ## Credits
353 |
354 | - [Sergey Karakhanyan](https://github.com/karakhanyans)
355 | - [Lionix Team](https://github.com/lionix-team)
356 |
357 |
358 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lionix/seo-manager",
3 | "description": "SEO Manager for Laravel Framework",
4 | "keywords": ["laravel", "seo", "seo-manager", "lionix", "meta", "meta-data", "open-graph", "og-data"],
5 | "type": "library",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Lionix Team",
10 | "email": "packages@lionix-team.com"
11 | },
12 | {
13 | "name": "Sergey Karakhanyan",
14 | "email": "karakhanyansa@gmail.com"
15 | }
16 | ],
17 | "extra": {
18 | "laravel": {
19 | "providers": [
20 | "Lionix\\SeoManager\\SeoManagerServiceProvider"
21 | ],
22 | "aliases": {
23 | "SeoManager": "Lionix\\SeoManager\\Facades\\SeoManager"
24 | }
25 | }
26 | },
27 | "autoload": {
28 | "psr-4": {
29 | "Lionix\\SeoManager\\": "src"
30 | },
31 | "files":[
32 | "src/helpers/helpers.php"
33 | ]
34 | },
35 | "minimum-stability": "dev",
36 | "require": {
37 | "php": "^7.0|^8.0",
38 | "illuminate/support": "5.5.* || 5.6.* || 5.7.* || 5.8.* || 6.* || 7.* || 8.*"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Commands/GenerateSeoManagerData.php:
--------------------------------------------------------------------------------
1 | info('Import Started');
43 | $this->importRoutes();
44 | $this->info('Import Finished');
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Controllers/ImportController.php:
--------------------------------------------------------------------------------
1 | importRoutes();
22 | return response()->json(['routes' => $routes]);
23 | } catch (\Exception $exception) {
24 | return response()->json(['status' => false, 'message' => $exception->getMessage()]);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Controllers/LocalesController.php:
--------------------------------------------------------------------------------
1 | json(['locales' => $locales]);
19 | }
20 |
21 | /**
22 | * @param Request $request
23 | * @return \Illuminate\Http\JsonResponse
24 | */
25 | public function addLocale(Request $request)
26 | {
27 | try{
28 | $locale = Locale::whereName($request->get('name'))->first();
29 | if(!$locale){
30 | $locale = new Locale();
31 | $locale->fill($request->all());
32 | $locale->save();
33 | return response()->json(['locale' => $locale->name]);
34 | }
35 | throw new \Exception('Locale is already exist');
36 | }catch (\Exception $exception){
37 | return response()->json(['status' => false, 'message' => $exception->getMessage()], 400);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Controllers/ManagerController.php:
--------------------------------------------------------------------------------
1 | get('locale')){
22 | app()->setLocale($request->get('locale'));
23 | $this->locale = app()->getLocale();
24 | }
25 | }
26 |
27 | /**
28 | * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
29 | */
30 | public function index()
31 | {
32 | return view('seo-manager::index');
33 | }
34 |
35 | /**
36 | * @return \Illuminate\Http\JsonResponse
37 | */
38 | public function getRoutes()
39 | {
40 | $routes = SeoManagerModel::all();
41 | return response()->json(['routes' => $routes]);
42 | }
43 |
44 | /**
45 | * @return \Illuminate\Http\JsonResponse
46 | */
47 | public function getModels()
48 | {
49 | try {
50 | $models = $this->getAllModels();
51 | return response()->json(['models' => $models]);
52 | } catch (\Exception $exception) {
53 | return response()->json(['status' => false, 'message' => $exception->getMessage()]);
54 | }
55 | }
56 |
57 | /**
58 | * @param Request $request
59 | * @return \Illuminate\Http\JsonResponse
60 | * @throws \Throwable
61 | */
62 | public function getModelColumns(Request $request)
63 | {
64 | try {
65 | $model = $request->get('model');
66 | $columns = $this->getColumns($model);
67 | return response()->json(['columns' => $columns]);
68 | } catch (\Exception $exception) {
69 | return response()->json(['status' => false, 'message' => $exception->getMessage()]);
70 | }
71 | }
72 |
73 | /**
74 | * @param Request $request
75 | * @return \Illuminate\Http\JsonResponse
76 | */
77 | public function storeData(Request $request)
78 | {
79 | $allowedColumns = Schema::getColumnListing(config('seo-manager.database.table'));
80 | try {
81 | $id = $request->get('id');
82 | $type = $request->get('type');
83 | $seoManager = SeoManagerModel::find($id);
84 | if (in_array($type, $allowedColumns)) {
85 | $data = $request->get($type);
86 | if($type != 'mapping' && $this->locale !== config('seo-manager.locale')){
87 | $translate = $seoManager->translation()->where('locale', $this->locale)->first();
88 | if(!$translate){
89 | $newInst = new Translate();
90 | $newInst->locale = $this->locale;
91 | $translate = $seoManager->translation()->save($newInst);
92 | }
93 | $translate->$type = $data;
94 | $translate->save();
95 | }else{
96 | $seoManager->$type = $data;
97 | $seoManager->save();
98 | }
99 | }
100 | return response()->json([$type => $seoManager->$type]);
101 | } catch (\Exception $exception) {
102 | return response()->json(['status' => false, 'message' => $exception->getMessage()]);
103 | }
104 | }
105 |
106 | /**
107 | * @param Request $request
108 | * @return \Illuminate\Http\JsonResponse
109 | */
110 | public function getExampleTitle(Request $request)
111 | {
112 | $route = $request->get('route');
113 | try {
114 | $manager = SeoManagerModel::find($route['id']);
115 | $titles = $route['title_dynamic'];
116 | $exampleTitle = $this->getDynamicTitle($titles, $manager);
117 | return response()->json(['example_title' => $exampleTitle]);
118 | } catch (\Exception $exception) {
119 | return response()->json(['status' => false, 'message' => $exception->getMessage()]);
120 | }
121 | }
122 |
123 | /**
124 | * @param Request $request
125 | * @return \Illuminate\Http\JsonResponse
126 | */
127 | public function deleteRoute(Request $request)
128 | {
129 | try {
130 | SeoManagerModel::destroy($request->id);
131 | return response()->json(['deleted' => true]);
132 | } catch (\Exception $exception) {
133 | return response()->json(['status' => false, 'message' => $exception->getMessage()]);
134 | }
135 | }
136 |
137 | /**
138 | * @param Request $request
139 | * @return array|null
140 | */
141 | public function sharingPreview(Request $request)
142 | {
143 | $id = $request->get('id');
144 | $seoManager = SeoManagerModel::find($id);
145 | if(is_null($seoManager)){
146 | return null;
147 | }
148 | $ogData = $this->getOgData($seoManager, null);
149 | return response()->json(['og_data' => $ogData]);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/Facades/SeoManager.php:
--------------------------------------------------------------------------------
1 | table = config('seo-manager.database.locales_table');
19 |
20 | parent::__construct();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Models/SeoManager.php:
--------------------------------------------------------------------------------
1 | 'array',
27 | 'mapping' => 'array',
28 | 'keywords' => 'array',
29 | 'title_dynamic' => 'array',
30 | 'og_data' => 'array',
31 | ];
32 |
33 | public function __construct()
34 | {
35 | $this->table = config('seo-manager.database.table');
36 | $this->locale = app()->getLocale();
37 |
38 | parent::__construct();
39 | }
40 |
41 | public function translation()
42 | {
43 | return $this->hasOne(Translate::class, 'route_id', 'id')->where('locale', app()->getLocale());
44 | }
45 |
46 | private function isNotDefaultLocale()
47 | {
48 | return $this->locale !== config('seo-manager.locale') && $this->has('translation');
49 | }
50 |
51 | public function getKeywordsAttribute($value)
52 | {
53 | if ($this->isNotDefaultLocale() && !empty(optional($this->translation)->keywords)) {
54 | return $this->translation->keywords;
55 | }
56 | return json_decode($value);
57 | }
58 |
59 | public function getDescriptionAttribute($value)
60 | {
61 | if ($this->isNotDefaultLocale() && !is_null(optional($this->translation)->description)) {
62 | return $this->translation->description;
63 | }
64 | return $value;
65 | }
66 |
67 | public function getTitleAttribute($value)
68 | {
69 | if ($this->isNotDefaultLocale() && !is_null(optional($this->translation)->title)) {
70 | return $this->translation->title;
71 | }
72 | return $value;
73 | }
74 |
75 | public function getAuthorAttribute($value)
76 | {
77 | if ($this->isNotDefaultLocale() && !is_null(optional($this->translation)->author)) {
78 | return $this->translation->author;
79 | }
80 | return $value;
81 | }
82 |
83 | public function getUrlAttribute($value)
84 | {
85 | if ($this->isNotDefaultLocale() && !is_null(optional($this->translation)->url)) {
86 | return $this->translation->url;
87 | }
88 | return $value;
89 | }
90 |
91 | public function getTitleDynamicAttribute($value)
92 | {
93 | if ($this->isNotDefaultLocale() && !empty(optional($this->translation)->title_dynamic)) {
94 | return $this->translation->title_dynamic;
95 | }
96 | return json_decode($value);
97 | }
98 |
99 | public function getOgDataAttribute($value)
100 | {
101 | if ($this->isNotDefaultLocale() && !is_null(optional($this->translation)->og_data)) {
102 | return $this->translation->og_data;
103 | }
104 | return json_decode($value, true);
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/Models/Translate.php:
--------------------------------------------------------------------------------
1 | 'array',
13 | 'title_dynamic' => 'array',
14 | 'og_data' => 'array',
15 | ];
16 |
17 | public function __construct()
18 | {
19 | $this->table = config('seo-manager.database.translates_table');
20 |
21 | parent::__construct();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/SeoManager.php:
--------------------------------------------------------------------------------
1 | getMetaData($property);
25 | }
26 |
27 | /**
28 | * Get Meta Keywords formatted
29 | * @return mixed
30 | */
31 | public function metaKeywords()
32 | {
33 | return $this->getMetaData('keywords');
34 | }
35 |
36 | /**
37 | * Get Title
38 | * @return mixed
39 | */
40 | public function metaTitle()
41 | {
42 | return $this->getMetaData('title');
43 |
44 | }
45 |
46 | /**
47 | * Get URL
48 | * @return mixed
49 | */
50 | public function metaUrl()
51 | {
52 | return $this->getMetaData('url');
53 |
54 | }
55 |
56 | /**
57 | * Get Meta Author
58 | * @return mixed
59 | */
60 | public function metaAuthor()
61 | {
62 | return $this->getMetaData('author');
63 | }
64 |
65 | /**
66 | * Get Meta Description
67 | * @return mixed
68 | */
69 | public function metaDescription()
70 | {
71 | return $this->getMetaData('description');
72 | }
73 |
74 | /**
75 | * Get dynamically generated title based on users mapping
76 | * @return mixed
77 | */
78 | public function metaTitleDynamic()
79 | {
80 | return $this->getMetaData('title_dynamic');
81 | }
82 |
83 | /**
84 | * Get Open Graph Data
85 | * @param $property
86 | * @return mixed
87 | */
88 | public function metaOpenGraph($property = null)
89 | {
90 | $og_data = $this->getMetaData('og_data');
91 | if (!is_null($property) && isset($og_data[$property])) {
92 | return $og_data[$property];
93 | }
94 | return $this->getMetaData('og_data');
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/SeoManagerServiceProvider.php:
--------------------------------------------------------------------------------
1 | loadRoutesFrom(__DIR__ . '/routes/seo-manager.php');
19 | $this->loadMigrationsFrom(__DIR__ . '/migrations');
20 | $this->loadViewsFrom(__DIR__ . '/views', 'seo-manager');
21 |
22 | $this->publishes([
23 | __DIR__ . '/config/seo-manager.php' => config_path('seo-manager.php'),
24 | ], 'config');
25 |
26 | $this->publishes([
27 | __DIR__ . '/assets' => public_path('vendor/lionix'),
28 | ], 'assets');
29 |
30 | $this->commands([
31 | GenerateSeoManagerData::class
32 | ]);
33 |
34 | $this->registerHelpers();
35 | $router = $this->app['router'];
36 | $router->pushMiddlewareToGroup('web', \Lionix\SeoManager\Middleware\ClearViewCache::class);
37 |
38 | if (config('seo-manager.shared_meta_data')) {
39 | $router->pushMiddlewareToGroup('web', \Lionix\SeoManager\Middleware\SeoManager::class);
40 | }
41 |
42 | // Blade Directives
43 | $this->registerBladeDirectives();
44 | }
45 |
46 | /**
47 | * Register services.
48 | *
49 | * @return void
50 | */
51 | public function register()
52 | {
53 | $this->mergeConfigFrom(
54 | __DIR__ . '/config/seo-manager.php', 'seo-manager'
55 | );
56 | $this->app->bind('seomanager', function () {
57 | return new SeoManager();
58 | });
59 | $this->app->alias('seomanager', SeoManager::class);
60 | }
61 |
62 | /**
63 | * Register helpers file
64 | */
65 | public function registerHelpers()
66 | {
67 | // Load the helpers
68 | if (file_exists($file = __DIR__ . '/helpers/helpers.php')) {
69 | require $file;
70 | }
71 | }
72 |
73 | /**
74 | * Register Blade Directives
75 | */
76 | public function registerBladeDirectives()
77 | {
78 | $names = [
79 | 'keywords',
80 | 'url',
81 | 'author',
82 | 'description',
83 | 'title',
84 | ];
85 |
86 | Blade::directive('meta', function ($expression) use ($names) {
87 | $meta = '';
88 | $expression = trim($expression, '\"\'');
89 | $metaData = metaData($expression);
90 | $type = in_array($expression, $names) ? "name" : "property";
91 |
92 | if (is_array($metaData)) {
93 | foreach ($metaData as $key => $og) {
94 | $type = in_array($key, $names) ? "name" : "property";
95 |
96 | $meta .= "\n";
97 | }
98 | } else {
99 | $meta .= "\n";
100 | }
101 | return $meta;
102 | });
103 | Blade::directive('keywords', function () {
104 | return "\n";
105 | });
106 | Blade::directive('url', function () {
107 | return "\n";
108 | });
109 | Blade::directive('author', function () {
110 | return "\n";
111 | });
112 | Blade::directive('description', function () {
113 | return "\n";
114 | });
115 | Blade::directive('title', function () {
116 | return "\n";
117 | });
118 | Blade::directive('openGraph', function ($expression) {
119 | $expression = trim($expression, '\"\'');
120 | $meta = '';
121 | $metaOpenGraph = metaOpenGraph($expression);
122 | if (is_array($metaOpenGraph)) {
123 | foreach ($metaOpenGraph as $key => $og) {
124 | $meta .= "\n";
125 | }
126 | } else {
127 | $meta .= "\n";
128 | }
129 | return $meta;
130 | });
131 | Blade::directive('titleDynamic', function () {
132 | return metaTitleDynamic();
133 | });
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/src/Traits/Appends.php:
--------------------------------------------------------------------------------
1 | appends;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Traits/SeoManagerTrait.php:
--------------------------------------------------------------------------------
1 | exceptRoutes = array_merge($this->exceptRoutes, config('seo-manager.except_routes'));
31 | $this->exceptColumns = array_merge($this->exceptColumns, config('seo-manager.except_columns'));
32 | }
33 |
34 | /**
35 | * Detect Parameters from the URI
36 | * @param $uri
37 | * @return mixed
38 | */
39 | private function getParamsFromURI($uri)
40 | {
41 | preg_match_all('/{(.*?)}/', $uri, $output_array);
42 |
43 | return $output_array[1];
44 | }
45 |
46 | /**
47 | * Check if the given data is URI param
48 | * @param $param
49 | * @return bool
50 | */
51 | private function isParam($param)
52 | {
53 | $pattern_params = '/{(.*?)}/';
54 | preg_match_all($pattern_params, $param, $output_params);
55 | if (!empty($output_params[1])) {
56 | return true;
57 | }
58 | return false;
59 | }
60 |
61 | /**
62 | * Check if the given data is Title
63 | * @param $param
64 | * @return bool
65 | */
66 | private function isTitle($param)
67 | {
68 | $pattern_title = '/~(.*?)~/';
69 | preg_match_all($pattern_title, $param, $pattern_title);
70 | if (!empty($pattern_title[1])) {
71 | return true;
72 | }
73 | return false;
74 | }
75 |
76 | /**
77 | * Remove unnecessary characters from param
78 | * @param $param
79 | * @return string
80 | */
81 | private function cleanParam($param)
82 | {
83 | return strtolower(str_replace(['{', '}'], '', $param));
84 | }
85 |
86 | /**
87 | * Remove routes which shouldn't be imported to Seo Manager
88 | * @return array
89 | */
90 | private function cleanRoutes()
91 | {
92 | $routes = \Route::getRoutes();
93 | $getRoutes = array_keys($routes->get('GET'));
94 | foreach ($getRoutes as $key => $route) {
95 | foreach ($this->exceptRoutes as $rule) {
96 | if (strpos($route, $rule) !== FALSE) {
97 | unset($getRoutes[$key]);
98 | }
99 | }
100 | }
101 | return $getRoutes;
102 | }
103 |
104 | /**
105 | * @return array
106 | * @throws \ReflectionException
107 | */
108 | private function getAllModels()
109 | {
110 | $path = base_path('app') . '/' . config('seo-manager.models_path');
111 |
112 | $models = File::allFiles($path);
113 | $cleanModelNames = [];
114 | foreach ($models as $model) {
115 | $modelPath = $this->cleanFilePath($model);
116 | $reflectionClass =(new \ReflectionClass($modelPath))->getParentClass();
117 | if($reflectionClass !== false){
118 | if($reflectionClass->getName() === "Illuminate\Database\Eloquent\Model" || $reflectionClass->getName() === "Illuminate\Foundation\Auth\User"){
119 | $cleanModel = [
120 | 'path' => $modelPath,
121 | 'name' => str_replace('.php', '', $model->getFilename())
122 | ];
123 | array_push($cleanModelNames, $cleanModel);
124 | }
125 | }
126 | }
127 | return $cleanModelNames;
128 | }
129 |
130 | /**
131 | * Get Model all Columns
132 | * @param $model
133 | * @return array
134 | */
135 | public function getColumns($model)
136 | {
137 | $appends = [];
138 | if (method_exists((new $model), 'getAppends')) {
139 | $appends = (new $model)->getAppends();
140 | }
141 | $table = (new $model)->getTable();
142 | $columns = $this->getCleanColumns(Schema::getColumnListing($table));
143 | return array_merge($columns, $appends);
144 | }
145 |
146 | /**
147 | * Clean model file path
148 | * @param $file
149 | * @return string
150 | */
151 | private function cleanFilePath($file)
152 | {
153 | return '\\' . ucfirst(str_replace('/', '\\', substr($file, strpos($file, 'app'), -4)));
154 | }
155 |
156 | /**
157 | * Remove unnecessary columns from table columns list
158 | * @param $columns
159 | * @return array
160 | */
161 | private function getCleanColumns($columns)
162 | {
163 | return array_diff($columns, $this->exceptColumns);
164 | }
165 |
166 | /**
167 | * Import Routes to SeoManager database
168 | * @return array|\Illuminate\Database\Eloquent\Collection|static[]
169 | */
170 | private function importRoutes()
171 | {
172 | $routes = $this->cleanRoutes();
173 | foreach ($routes as $uri) {
174 | $data = [
175 | 'uri' => $uri,
176 | 'params' => $this->getParamsFromURI($uri),
177 | 'keywords' => [],
178 | 'title_dynamic' => []
179 | ];
180 | if (!SeoManager::where('uri', $uri)->first()) {
181 | $seoManager = new SeoManager();
182 | $seoManager->fill($data);
183 | $seoManager->save();
184 | }
185 | }
186 |
187 | $routes = SeoManager::all();
188 | return $routes;
189 | }
190 |
191 | /**
192 | * Get mapped Seo Data from Database for Current Route
193 | * @param $property
194 | * @return mixed
195 | */
196 | private function getMetaData($property)
197 | {
198 | $route = \Route::current();
199 | if (is_null($route)){
200 | return null;
201 | }
202 | $uri = $route->uri();
203 | $seoManager = SeoManager::where('uri', $uri)->first();
204 | if(is_null($seoManager)){
205 | return null;
206 | }
207 | $metaData = [];
208 | if(count($seoManager->keywords) > 0){
209 | $metaData['keywords'] = implode(', ', $seoManager->keywords);
210 | }
211 | if($seoManager->description){
212 | $metaData['description'] = $seoManager->description;
213 | }
214 | if($seoManager->title){
215 | $metaData['title'] = $seoManager->title;
216 | }
217 | if($seoManager->url){
218 | $metaData['url'] = $seoManager->url;
219 | }else{
220 | $metaData['url'] = url()->full();
221 | }
222 | if($seoManager->author){
223 | $metaData['author'] = $seoManager->author;
224 | }
225 | if ($seoManager->mapping !== null) {
226 | $metaData['title_dynamic'] = $this->getDynamicTitle($seoManager->title_dynamic, $seoManager, $route->parameters);
227 | }
228 | if ($seoManager->og_data) {
229 | $ogData = $this->getOgData($seoManager, $route->parameters);
230 | if($property === 'og_data'){
231 | $metaData['og_data'] = $ogData;
232 | }else{
233 | foreach ($ogData as $key => $og) {
234 | $metaData[$key] = $og;
235 | }
236 | }
237 | }
238 |
239 | if($property !== null && !empty($property)){
240 | if(isset($metaData[$property])){
241 | return $metaData[$property];
242 | }else{
243 | return null;
244 | }
245 | }
246 | return $metaData;
247 | }
248 |
249 | /**
250 | * Get dynamic title based on user configs for current route
251 | * @param $params
252 | * @param $manager
253 | * @param $routeParams
254 | * @return string
255 | */
256 | private function getDynamicTitle($params, $manager, $routeParams = null)
257 | {
258 | $dynamicTitle = '';
259 | if(is_array($params)){
260 | foreach ($params as $param) {
261 | if ($this->isParam($param)) {
262 | $param = $this->cleanParam($param);
263 | $paramsArray = explode('-', $param);
264 | $mapping = $manager->mapping[$paramsArray[0]];
265 | $model = $mapping['model']['path'];
266 | $findBy = $mapping['find_by'];
267 | $selectedColumns = $mapping['selectedColumns'];
268 |
269 | if (in_array($paramsArray[1], $selectedColumns)) {
270 | $mappedTitle = (new $model);
271 |
272 | if ($routeParams) {
273 | $value = is_a($routeParams[$paramsArray[0]], $model) ? $routeParams[$paramsArray[0]]->$findBy : $routeParams[$paramsArray[0]];
274 | $mappedTitle = $mappedTitle->where($findBy, $value)->first();
275 | } else {
276 | $mappedTitle = $mappedTitle->first();
277 | }
278 |
279 | if ($mappedTitle) {
280 | $dynamicTitle .= optional($mappedTitle)->{$paramsArray[1]} . ' ';
281 | }
282 | }
283 | } elseif ($this->isTitle($param)) {
284 | $dynamicTitle .= $manager->title . ' ';
285 | } else {
286 | $dynamicTitle .= $param . ' ';
287 | }
288 | }
289 | }
290 | return $dynamicTitle;
291 | }
292 |
293 | /**
294 | * Get Open Graph Dynamic Data
295 | * @param $seoManager
296 | * @param $routeParams
297 | * @return array
298 | */
299 | private function getOgData($seoManager, $routeParams)
300 | {
301 | $dataArray = [];
302 | $value = '';
303 | foreach ($seoManager->og_data as $key => $og) {
304 | if (is_array(reset($og['data']))) {
305 | foreach ($og['data'] as $ogKey => $data) {
306 | if ($data['mapped']) {
307 | $value = $this->getMappedValue($data['value'], $seoManager, $routeParams);
308 | } elseif ($data['value']) {
309 | $value = $data['value'];
310 | }
311 | if ($data['value']) {
312 | $dataArray['og:' . $key . ':' . $ogKey] = $value;
313 | }
314 | }
315 | } else {
316 | if ($og['data']['mapped']) {
317 | $value = $this->getMappedValue($og['data']['value'], $seoManager, $routeParams);
318 | } elseif ($og['data']['value']) {
319 | $value = $og['data']['value'];
320 | } elseif ($key === 'url') {
321 | $value = url()->full();
322 | }
323 | if ($og['data']['value'] || $key === 'url') {
324 | $dataArray['og:' . $key] = $value;
325 | }
326 | }
327 | }
328 | return $dataArray;
329 | }
330 |
331 | /**
332 | * Get Open Graph Data Values based on Mapped Params
333 | * @param $value
334 | * @param $manager
335 | * @param $routeParams
336 | * @return mixed
337 | */
338 | private function getMappedValue($value, $manager, $routeParams)
339 | {
340 | $paramsArray = explode('-', strtolower($value));
341 | $mapping = $manager->mapping[$paramsArray[0]];
342 | $model = $mapping['model']['path'];
343 | $findBy = $mapping['find_by'];
344 | $selectedColumns = $mapping['selectedColumns'];
345 | $mapped = null;
346 | if (in_array($paramsArray[1], $selectedColumns)) {
347 | $mapped = (new $model);
348 | if ($routeParams) {
349 | $mapped = $mapped->where($findBy, $routeParams[$paramsArray[0]])->first();
350 | }else{
351 | $mapped = $mapped->first();
352 | }
353 | }
354 | return optional($mapped)->{$paramsArray[1]};
355 | }
356 | }
357 |
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-medium.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-medium.eot
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-medium.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-medium.woff
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-mediumd41d.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-mediumd41d.eot
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-regular.eot
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-regular.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-regular.woff
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-regulard41d.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-regulard41d.eot
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-semibold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-semibold.eot
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-semibold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-semibold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-semibold.woff
--------------------------------------------------------------------------------
/src/assets/fonts/cerebrisans/cerebrisans-semiboldd41d.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/cerebrisans/cerebrisans-semiboldd41d.eot
--------------------------------------------------------------------------------
/src/assets/fonts/feather/feather.min.css:
--------------------------------------------------------------------------------
1 | @font-face{font-family:Feather;src:url(fonts/Feather144f.ttf?sdxovp) format("truetype"),url(fonts/Feather144f.woff?sdxovp) format("woff"),url(fonts/Feather144f.svg?sdxovp#Feather) format("svg");font-weight:400;font-style:normal}.fe{font-family:Feather!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fe-activity:before{content:"\e900"}.fe-airplay:before{content:"\e901"}.fe-alert-circle:before{content:"\e902"}.fe-alert-octagon:before{content:"\e903"}.fe-alert-triangle:before{content:"\e904"}.fe-align-center:before{content:"\e905"}.fe-align-justify:before{content:"\e906"}.fe-align-left:before{content:"\e907"}.fe-align-right:before{content:"\e908"}.fe-anchor:before{content:"\e909"}.fe-aperture:before{content:"\e90a"}.fe-archive:before{content:"\e90b"}.fe-arrow-down:before{content:"\e90c"}.fe-arrow-down-circle:before{content:"\e90d"}.fe-arrow-down-left:before{content:"\e90e"}.fe-arrow-down-right:before{content:"\e90f"}.fe-arrow-left:before{content:"\e910"}.fe-arrow-left-circle:before{content:"\e911"}.fe-arrow-right:before{content:"\e912"}.fe-arrow-right-circle:before{content:"\e913"}.fe-arrow-up:before{content:"\e914"}.fe-arrow-up-circle:before{content:"\e915"}.fe-arrow-up-left:before{content:"\e916"}.fe-arrow-up-right:before{content:"\e917"}.fe-at-sign:before{content:"\e918"}.fe-award:before{content:"\e919"}.fe-bar-chart:before{content:"\e91a"}.fe-bar-chart-2:before{content:"\e91b"}.fe-battery:before{content:"\e91c"}.fe-battery-charging:before{content:"\e91d"}.fe-bell:before{content:"\e91e"}.fe-bell-off:before{content:"\e91f"}.fe-bluetooth:before{content:"\e920"}.fe-bold:before{content:"\e921"}.fe-book:before{content:"\e922"}.fe-book-open:before{content:"\e923"}.fe-bookmark:before{content:"\e924"}.fe-box:before{content:"\e925"}.fe-briefcase:before{content:"\e926"}.fe-calendar:before{content:"\e927"}.fe-camera:before{content:"\e928"}.fe-camera-off:before{content:"\e929"}.fe-cast:before{content:"\e92a"}.fe-check:before{content:"\e92b"}.fe-check-circle:before{content:"\e92c"}.fe-check-square:before{content:"\e92d"}.fe-chevron-down:before{content:"\e92e"}.fe-chevron-left:before{content:"\e92f"}.fe-chevron-right:before{content:"\e930"}.fe-chevron-up:before{content:"\e931"}.fe-chevrons-down:before{content:"\e932"}.fe-chevrons-left:before{content:"\e933"}.fe-chevrons-right:before{content:"\e934"}.fe-chevrons-up:before{content:"\e935"}.fe-chrome:before{content:"\e936"}.fe-circle:before{content:"\e937"}.fe-clipboard:before{content:"\e938"}.fe-clock:before{content:"\e939"}.fe-cloud:before{content:"\e93a"}.fe-cloud-drizzle:before{content:"\e93b"}.fe-cloud-lightning:before{content:"\e93c"}.fe-cloud-off:before{content:"\e93d"}.fe-cloud-rain:before{content:"\e93e"}.fe-cloud-snow:before{content:"\e93f"}.fe-code:before{content:"\e940"}.fe-codepen:before{content:"\e941"}.fe-command:before{content:"\e942"}.fe-compass:before{content:"\e943"}.fe-copy:before{content:"\e944"}.fe-corner-down-left:before{content:"\e945"}.fe-corner-down-right:before{content:"\e946"}.fe-corner-left-down:before{content:"\e947"}.fe-corner-left-up:before{content:"\e948"}.fe-corner-right-down:before{content:"\e949"}.fe-corner-right-up:before{content:"\e94a"}.fe-corner-up-left:before{content:"\e94b"}.fe-corner-up-right:before{content:"\e94c"}.fe-cpu:before{content:"\e94d"}.fe-credit-card:before{content:"\e94e"}.fe-crop:before{content:"\e94f"}.fe-crosshair:before{content:"\e950"}.fe-database:before{content:"\e951"}.fe-delete:before{content:"\e952"}.fe-disc:before{content:"\e953"}.fe-dollar-sign:before{content:"\e954"}.fe-download:before{content:"\e955"}.fe-download-cloud:before{content:"\e956"}.fe-droplet:before{content:"\e957"}.fe-edit:before{content:"\e958"}.fe-edit-2:before{content:"\e959"}.fe-edit-3:before{content:"\e95a"}.fe-external-link:before{content:"\e95b"}.fe-eye:before{content:"\e95c"}.fe-eye-off:before{content:"\e95d"}.fe-facebook:before{content:"\e95e"}.fe-fast-forward:before{content:"\e95f"}.fe-feather:before{content:"\e960"}.fe-file:before{content:"\e961"}.fe-file-minus:before{content:"\e962"}.fe-file-plus:before{content:"\e963"}.fe-file-text:before{content:"\e964"}.fe-film:before{content:"\e965"}.fe-filter:before{content:"\e966"}.fe-flag:before{content:"\e967"}.fe-folder:before{content:"\e968"}.fe-folder-minus:before{content:"\e969"}.fe-folder-plus:before{content:"\e96a"}.fe-gift:before{content:"\e96b"}.fe-git-branch:before{content:"\e96c"}.fe-git-commit:before{content:"\e96d"}.fe-git-merge:before{content:"\e96e"}.fe-git-pull-request:before{content:"\e96f"}.fe-github:before{content:"\e970"}.fe-gitlab:before{content:"\e971"}.fe-globe:before{content:"\e972"}.fe-grid:before{content:"\e973"}.fe-hard-drive:before{content:"\e974"}.fe-hash:before{content:"\e975"}.fe-headphones:before{content:"\e976"}.fe-heart:before{content:"\e977"}.fe-help-circle:before{content:"\e978"}.fe-home:before{content:"\e979"}.fe-image:before{content:"\e97a"}.fe-inbox:before{content:"\e97b"}.fe-info:before{content:"\e97c"}.fe-instagram:before{content:"\e97d"}.fe-italic:before{content:"\e97e"}.fe-layers:before{content:"\e97f"}.fe-layout:before{content:"\e980"}.fe-life-buoy:before{content:"\e981"}.fe-link:before{content:"\e982"}.fe-link-2:before{content:"\e983"}.fe-linkedin:before{content:"\e984"}.fe-list:before{content:"\e985"}.fe-loader:before{content:"\e986"}.fe-lock:before{content:"\e987"}.fe-log-in:before{content:"\e988"}.fe-log-out:before{content:"\e989"}.fe-mail:before{content:"\e98a"}.fe-map:before{content:"\e98b"}.fe-map-pin:before{content:"\e98c"}.fe-maximize:before{content:"\e98d"}.fe-maximize-2:before{content:"\e98e"}.fe-menu:before{content:"\e98f"}.fe-message-circle:before{content:"\e990"}.fe-message-square:before{content:"\e991"}.fe-mic:before{content:"\e992"}.fe-mic-off:before{content:"\e993"}.fe-minimize:before{content:"\e994"}.fe-minimize-2:before{content:"\e995"}.fe-minus:before{content:"\e996"}.fe-minus-circle:before{content:"\e997"}.fe-minus-square:before{content:"\e998"}.fe-monitor:before{content:"\e999"}.fe-moon:before{content:"\e99a"}.fe-more-horizontal:before{content:"\e99b"}.fe-more-vertical:before{content:"\e99c"}.fe-move:before{content:"\e99d"}.fe-music:before{content:"\e99e"}.fe-navigation:before{content:"\e99f"}.fe-navigation-2:before{content:"\e9a0"}.fe-octagon:before{content:"\e9a1"}.fe-package:before{content:"\e9a2"}.fe-paperclip:before{content:"\e9a3"}.fe-pause:before{content:"\e9a4"}.fe-pause-circle:before{content:"\e9a5"}.fe-percent:before{content:"\e9a6"}.fe-phone:before{content:"\e9a7"}.fe-phone-call:before{content:"\e9a8"}.fe-phone-forwarded:before{content:"\e9a9"}.fe-phone-incoming:before{content:"\e9aa"}.fe-phone-missed:before{content:"\e9ab"}.fe-phone-off:before{content:"\e9ac"}.fe-phone-outgoing:before{content:"\e9ad"}.fe-pie-chart:before{content:"\e9ae"}.fe-play:before{content:"\e9af"}.fe-play-circle:before{content:"\e9b0"}.fe-plus:before{content:"\e9b1"}.fe-plus-circle:before{content:"\e9b2"}.fe-plus-square:before{content:"\e9b3"}.fe-pocket:before{content:"\e9b4"}.fe-power:before{content:"\e9b5"}.fe-printer:before{content:"\e9b6"}.fe-radio:before{content:"\e9b7"}.fe-refresh-ccw:before{content:"\e9b8"}.fe-refresh-cw:before{content:"\e9b9"}.fe-repeat:before{content:"\e9ba"}.fe-rewind:before{content:"\e9bb"}.fe-rotate-ccw:before{content:"\e9bc"}.fe-rotate-cw:before{content:"\e9bd"}.fe-rss:before{content:"\e9be"}.fe-save:before{content:"\e9bf"}.fe-scissors:before{content:"\e9c0"}.fe-search:before{content:"\e9c1"}.fe-send:before{content:"\e9c2"}.fe-server:before{content:"\e9c3"}.fe-settings:before{content:"\e9c4"}.fe-share:before{content:"\e9c5"}.fe-share-2:before{content:"\e9c6"}.fe-shield:before{content:"\e9c7"}.fe-shield-off:before{content:"\e9c8"}.fe-shopping-bag:before{content:"\e9c9"}.fe-shopping-cart:before{content:"\e9ca"}.fe-shuffle:before{content:"\e9cb"}.fe-sidebar:before{content:"\e9cc"}.fe-skip-back:before{content:"\e9cd"}.fe-skip-forward:before{content:"\e9ce"}.fe-slack:before{content:"\e9cf"}.fe-slash:before{content:"\e9d0"}.fe-sliders:before{content:"\e9d1"}.fe-smartphone:before{content:"\e9d2"}.fe-speaker:before{content:"\e9d3"}.fe-square:before{content:"\e9d4"}.fe-star:before{content:"\e9d5"}.fe-stop-circle:before{content:"\e9d6"}.fe-sun:before{content:"\e9d7"}.fe-sunrise:before{content:"\e9d8"}.fe-sunset:before{content:"\e9d9"}.fe-tablet:before{content:"\e9da"}.fe-tag:before{content:"\e9db"}.fe-target:before{content:"\e9dc"}.fe-terminal:before{content:"\e9dd"}.fe-thermometer:before{content:"\e9de"}.fe-thumbs-down:before{content:"\e9df"}.fe-thumbs-up:before{content:"\e9e0"}.fe-toggle-left:before{content:"\e9e1"}.fe-toggle-right:before{content:"\e9e2"}.fe-trash:before{content:"\e9e3"}.fe-trash-2:before{content:"\e9e4"}.fe-trending-down:before{content:"\e9e5"}.fe-trending-up:before{content:"\e9e6"}.fe-triangle:before{content:"\e9e7"}.fe-truck:before{content:"\e9e8"}.fe-tv:before{content:"\e9e9"}.fe-twitter:before{content:"\e9ea"}.fe-type:before{content:"\e9eb"}.fe-umbrella:before{content:"\e9ec"}.fe-underline:before{content:"\e9ed"}.fe-unlock:before{content:"\e9ee"}.fe-upload:before{content:"\e9ef"}.fe-upload-cloud:before{content:"\e9f0"}.fe-user:before{content:"\e9f1"}.fe-user-check:before{content:"\e9f2"}.fe-user-minus:before{content:"\e9f3"}.fe-user-plus:before{content:"\e9f4"}.fe-user-x:before{content:"\e9f5"}.fe-users:before{content:"\e9f6"}.fe-video:before{content:"\e9f7"}.fe-video-off:before{content:"\e9f8"}.fe-voicemail:before{content:"\e9f9"}.fe-volume:before{content:"\e9fa"}.fe-volume-1:before{content:"\e9fb"}.fe-volume-2:before{content:"\e9fc"}.fe-volume-x:before{content:"\e9fd"}.fe-watch:before{content:"\e9fe"}.fe-wifi:before{content:"\e9ff"}.fe-wifi-off:before{content:"\ea00"}.fe-wind:before{content:"\ea01"}.fe-x:before{content:"\ea02"}.fe-x-circle:before{content:"\ea03"}.fe-x-square:before{content:"\ea04"}.fe-youtube:before{content:"\ea05"}.fe-zap:before{content:"\ea06"}.fe-zap-off:before{content:"\ea07"}.fe-zoom-in:before{content:"\ea08"}.fe-zoom-out:before{content:"\ea09"}
--------------------------------------------------------------------------------
/src/assets/fonts/feather/fonts/Feather144f.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/feather/fonts/Feather144f.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/feather/fonts/Feather144f.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/fonts/feather/fonts/Feather144f.woff
--------------------------------------------------------------------------------
/src/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/img/logo.png
--------------------------------------------------------------------------------
/src/assets/img/logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/img/nophoto.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lionix-team/seo-manager/23761959505464044b13db04e6e10e5af2f74c7e/src/assets/img/nophoto.png
--------------------------------------------------------------------------------
/src/assets/js/theme.min.js:
--------------------------------------------------------------------------------
1 | "use strict";var Demo=function(){var n=document.querySelector("#demoForm"),s=document.querySelector("#topnav"),i=document.querySelector("#topbar"),c=document.querySelector("#sidebar"),d=document.querySelector("#sidebarUser"),u=document.querySelectorAll('[class^="container"]'),p=(document.querySelectorAll("#stylesheetLight, #stylesheetDark"),document.querySelector("#stylesheetLight")),h=document.querySelector("#stylesheetDark"),b=localStorage.getItem("dashkitNavPosition")?localStorage.getItem("dashkitNavPosition"):"sidenav",v=localStorage.getItem("dashkitSidebarColor")?localStorage.getItem("dashkitSidebarColor"):"default";document.addEventListener("DOMContentLoaded",function(){var a,e,t,o,r,l;"light"==(a=colorScheme)?(p.disabled=!1,h.disabled=!0):"dark"==a&&(p.disabled=!0,h.disabled=!1),function(a){if(s&&i&&c&&d)if("topnav"==a){t(i),t(c);for(var e=0;e'+t+""),r+=''+l+o+n+""}}}(a,t),t.update()}return window.Chart&&r(Chart,(a={defaults:{global:{responsive:!0,maintainAspectRatio:!1,defaultColor:"dark"==colorScheme?o.gray[700]:o.gray[600],defaultFontColor:"dark"==colorScheme?o.gray[700]:o.gray[600],defaultFontFamily:t.base,defaultFontSize:13,layout:{padding:0},legend:{display:!1,position:"bottom",labels:{usePointStyle:!0,padding:16}},elements:{point:{radius:0,backgroundColor:o.primary[700]},line:{tension:.4,borderWidth:3,borderColor:o.primary[700],backgroundColor:o.transparent,borderCapStyle:"rounded"},rectangle:{backgroundColor:o.primary[700]},arc:{backgroundColor:o.primary[700],borderColor:"dark"==colorScheme?o.gray[800]:o.white,borderWidth:4}},tooltips:{enabled:!1,mode:"index",intersect:!1,custom:function(r){var a=$("#chart-tooltip");if(a.length||(a=$(''),$("body").append(a)),0!==r.opacity){if(r.body){var e=r.title||[],l=r.body.map(function(a){return a.lines}),n="";n+='',e.forEach(function(a){n+='"}),l.forEach(function(a,e){var t='',o=1'+t+a+""}),a.html(n)}var t=$(this._chart.canvas),o=(t.outerWidth(),t.outerHeight(),t.offset().top),s=t.offset().left,i=a.outerWidth(),c=a.outerHeight(),d=o+r.caretY-c-16,u=s+r.caretX-i/2;a.css({top:d+"px",left:u+"px",display:"block"})}else a.css("display","none")},callbacks:{label:function(a,e){var t=e.datasets[a.datasetIndex].label||"",o=a.yLabel,r="";return 1'+t+""),r+=''+o+""}}}},doughnut:{cutoutPercentage:83,tooltips:{callbacks:{title:function(a,e){return e.labels[a[0].index]},label:function(a,e){var t="";return t+=''+e.datasets[0].data[a.index]+""}}},legendCallback:function(a){var o=a.data,r="";return o.labels.forEach(function(a,e){var t=o.datasets[0].backgroundColor[e];r+='',r+='',r+=a,r+=""}),r}}}},Chart.scaleService.updateScaleDefaults("linear",{gridLines:{borderDash:[2],borderDashOffset:[2],color:"dark"==colorScheme?o.gray[900]:o.gray[300],drawBorder:!1,drawTicks:!1,lineWidth:0,zeroLineWidth:0,zeroLineColor:"dark"==colorScheme?o.gray[900]:o.gray[300],zeroLineBorderDash:[2],zeroLineBorderDashOffset:[2]},ticks:{beginAtZero:!0,padding:10,callback:function(a){if(!(a%10))return a}}}),Chart.scaleService.updateScaleDefaults("category",{gridLines:{drawBorder:!1,drawOnChartArea:!1,drawTicks:!1},ticks:{padding:20},maxBarThickness:10}),a)),e.on({change:function(){var a=$(this);a.is("[data-add]")&&l(a)},click:function(){var a=$(this);a.is("[data-update]")&&n(a)}}),{colors:o,fonts:t,colorScheme:colorScheme}}(),Header=function(){var a,e,t=$("#headerChart");t.length&&(a=t,e=new Chart(a,{type:"line",options:{scales:{yAxes:[{gridLines:{color:ThemeCharts.colors.gray[900],zeroLineColor:ThemeCharts.colors.gray[900]},ticks:{callback:function(a){if(!(a%10))return"$"+a+"k"}}}]},tooltips:{callbacks:{label:function(a,e){var t=e.datasets[a.datasetIndex].label||"",o=a.yLabel,r="";return 1'+t+""),r+='$'+o+"k"}}}},data:{labels:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Performance",data:[0,10,5,15,10,20,15,25,20,30,25,40]}]}}),a.data("chart",e))}(),Performance=function(){var a,e,t=$("#performanceChart");t.length&&(a=t,e=new Chart(a,{type:"line",options:{scales:{yAxes:[{ticks:{callback:function(a){if(!(a%10))return"$"+a+"k"}}}]},tooltips:{callbacks:{label:function(a,e){var t=e.datasets[a.datasetIndex].label||"",o=a.yLabel,r="";return 1'+t+""),r+='$'+o+"k"}}}},data:{labels:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Performance",data:[0,10,5,15,10,20,15,25,20,30,25,40]}]}}),a.data("chart",e))}(),PerformanceAlias=function(){var a,e,t=$("#performanceChartAlias");t.length&&(a=t,e=new Chart(a,{type:"line",options:{scales:{yAxes:[{ticks:{callback:function(a){if(!(a%10))return"$"+a+"k"}}}]},tooltips:{callbacks:{label:function(a,e){var t=e.datasets[a.datasetIndex].label||"",o=a.yLabel,r="";return 1'+t+""),r+='$'+o+"k"}}}},data:{labels:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Performance",data:[0,10,5,15,10,20,15,25,20,30,25,40]}]}}),a.data("chart",e))}(),Orders=function(){var a,e,t=$("#ordersChart"),o=$('[name="ordersSelect"]');t.length&&(a=t,e=new Chart(a,{type:"bar",options:{scales:{yAxes:[{ticks:{callback:function(a){if(!(a%10))return"$"+a+"k"}}}]},tooltips:{callbacks:{label:function(a,e){var t=e.datasets[a.datasetIndex].label||"",o=a.yLabel,r="";return 1'+t+""),r+='$'+o+"k"}}}},data:{labels:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Sales",data:[25,20,30,22,17,10,18,26,28,26,20,32]}]}}),a.data("chart",e)),o.on("change",function(){var a;"ordersSelectAll"==(a=$(this)).attr("id")&&(a.is(":checked")?o.prop("checked",!0):o.prop("checked",!1))})}(),OrdersAlias=function(){var a,e,t=$("#ordersChartAlias");t.length&&(a=t,e=new Chart(a,{type:"bar",options:{scales:{yAxes:[{ticks:{callback:function(a){if(!(a%10))return"$"+a+"k"}}}]},tooltips:{callbacks:{label:function(a,e){var t=e.datasets[a.datasetIndex].label||"",o=a.yLabel,r="";return 1'+t+""),r+='$'+o+"k"}}}},data:{labels:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Sales",data:[25,20,30,22,17,10,18,26,28,26,20,32]}]}}),a.data("chart",e))}(),Devices=function(){var a,e,t,o,r,l=$("#devicesChart");l.length&&(o=l,r=new Chart(o,{type:"doughnut",options:{tooltips:{callbacks:{title:function(a,e){return e.labels[a[0].index]},label:function(a,e){var t="";return t+=''+e.datasets[0].data[a.index]+"%"}}}},data:{labels:["Desktop","Tablet","Mobile"],datasets:[{data:[60,25,15],backgroundColor:[ThemeCharts.colors.primary[700],ThemeCharts.colors.primary[300],ThemeCharts.colors.primary[100]],hoverBorderColor:"dark"==ThemeCharts.colorScheme?ThemeCharts.colors.gray[800]:ThemeCharts.colors.white}]}}),o.data("chart",r),e=(a=l).data("chart").generateLegend(),t=a.data("target"),$(t).html(e))}(),WeeklyHours=function(){var a,e,t=$("#weeklyHoursChart");t.length&&(a=t,e=new Chart(a,{type:"bar",options:{scales:{yAxes:[{ticks:{callback:function(a){if(!(a%10))return a+"hrs"}}}]},tooltips:{callbacks:{label:function(a,e){var t=e.datasets[a.datasetIndex].label||"",o=a.yLabel,r="";return 1'+t+""),r+=''+o+"hrs"}}}},data:{labels:["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],datasets:[{data:[21,12,28,15,5,12,17,2]}]}}),a.data("chart",e))}(),Dropdowns=function(){var e=$(".dropup, .dropright, .dropdown, .dropleft"),t=$(".dropdown-menu"),o=$(".dropdown-menu .dropdown-menu");$(".dropdown-menu .dropdown-toggle").on("click",function(){var a;return(a=$(this)).closest(e).siblings(e).find(t).removeClass("show"),a.next(o).toggleClass("show"),!1}),e.on("hide.bs.dropdown",function(){var a,e;a=$(this),(e=a.find(o)).length&&e.removeClass("show")})}(),Dropzones=function(){var a=$('[data-toggle="dropzone"]'),l=$(".dz-preview");a.length&&(Dropzone.autoDiscover=!1,a.each(function(){var a,e,t,o,r;a=$(this),e=void 0!==a.data("dropzone-multiple"),t=a.find(l),o=void 0,r={url:a.data("dropzone-url"),thumbnailWidth:null,thumbnailHeight:null,previewsContainer:t.get(0),previewTemplate:t.html(),maxFiles:e?null:1,acceptedFiles:e?null:"image/*",init:function(){this.on("addedfile",function(a){!e&&o&&this.removeFile(o),o=a})}},t.html(""),a.dropzone(r)}))}(),Flatpickr=function(){var a=$('[data-toggle="flatpickr"]');a.length&&a.each(function(){var a,e;a=$(this),e={mode:void 0!==a.data("flatpickr-mode")?a.data("flatpickr-mode"):"single"},a.flatpickr(e)})}(),Highlight=void $(".highlight").each(function(a,e){var t;t=e,hljs.highlightBlock(t)}),Lists=function(){var a=$('[data-toggle="lists"]'),e=$("[data-sort]");a.length&&a.each(function(){var a,e;a=$(this),new List(a.get(0),{valueNames:(e=a).data("lists-values"),listClass:e.data("lists-class")?e.data("lists-class"):"list"})}),e.on("click",function(){return!1})}(),Navbar=function(){var e=$(".navbar-nav, .navbar-nav .nav"),t=$(".navbar-nav .collapse");t.on({"show.bs.collapse":function(){var a;(a=$(this)).closest(e).find(t).not(a).collapse("hide")}})}(),Popover=function(){var a=$('[data-toggle="popover"]');a.length&&a.popover()}(),Quill=function(){var a=$('[data-toggle="quill"]');a.length&&a.each(function(){var a,e;a=$(this),e=a.data("quill-placeholder"),new Quill(a.get(0),{modules:{toolbar:[["bold","italic"],["link","blockquote","code","image"],[{list:"ordered"},{list:"bullet"}]]},placeholder:e,theme:"snow"})})}(),Select2=function(){var a=$('[data-toggle="select"]');function t(a){if(!a.id)return a.text;var e=$(a.element).data("avatar-src");return e?$('
'+a.text+""):a.text}a.length&&a.each(function(){var a,e;a=$(this),e={dropdownParent:a.closest(".modal").length?a.closest(".modal"):$(document.body),minimumResultsForSearch:a.data("minimum-results-for-search"),templateResult:t},a.select2(e)})}(),Tooltip=function(){var a=$('[data-toggle="tooltip"]');a.length&&a.tooltip()}();
--------------------------------------------------------------------------------
/src/config/seo-manager.php:
--------------------------------------------------------------------------------
1 | [
8 | 'table' => 'seo_manager',
9 | 'locales_table' => 'seo_manager_locales',
10 | 'translates_table' => 'seo_manager_translates'
11 | ],
12 |
13 | /**
14 | * Set default locale,
15 | * It will be added as default locale
16 | * when locales table migrated
17 | */
18 | 'locale' => 'en',
19 |
20 | /**
21 | * Path where your eloquent models are
22 | * Leave this config empty if you want to look for models in whole project
23 | */
24 | 'models_path' => '',
25 |
26 | /**
27 | * Route from which your Dashboard will be available
28 | */
29 | 'route' => 'seo-manager',
30 |
31 | /**
32 | * Middleware array for dashboard
33 | * to prevent unauthorized users visit the manager
34 | */
35 | 'middleware' => [
36 | 'web'
37 | // 'auth',
38 | ],
39 |
40 | /**
41 | * Routes which shouldn't be imported to seo manager
42 | */
43 | 'except_routes' => [
44 | 'seo-manager',
45 | 'admin'
46 | //
47 | ],
48 |
49 | /**
50 | * Columns which shouldn't be used ( in mapping )
51 | */
52 | 'except_columns' => [
53 | // "created_at",
54 | // "updated_at",
55 | ],
56 |
57 | /**
58 | * Set this parameter to true
59 | * if you want to have "$metaData" variable
60 | * shared between all views in "web" middleware group
61 | */
62 | 'shared_meta_data' => false
63 | ];
64 |
--------------------------------------------------------------------------------
/src/helpers/helpers.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | $table->string('uri')->nullable();
19 | $table->jsonb('params')->nullable();
20 | $table->jsonb('mapping')->nullable();
21 | $table->jsonb('keywords');
22 | $table->string('description')->nullable();
23 | $table->string('title')->nullable();
24 | $table->string('author')->nullable();
25 | $table->string('url')->nullable();
26 | $table->jsonb('title_dynamic')->nullable();
27 | $table->jsonb('og_data')->nullable();
28 | $table->timestamps();
29 | });
30 | }
31 |
32 | /**
33 | * Reverse the migrations.
34 | *
35 | * @return void
36 | */
37 | public function down()
38 | {
39 | Schema::dropIfExists(config('seo-manager.database.table'));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/migrations/2019_01_08_132731_create_locales_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | $table->string('name');
19 | $table->timestamps();
20 | });
21 |
22 | \Illuminate\Support\Facades\DB::table(config('seo-manager.database.locales_table'))->insert([
23 | 'name' => config('seo-manager.locale'),
24 | 'created_at' => \Carbon\Carbon::now(),
25 | 'updated_at' => \Carbon\Carbon::now()
26 | ]);
27 | }
28 |
29 | /**
30 | * Reverse the migrations.
31 | *
32 | * @return void
33 | */
34 | public function down()
35 | {
36 | Schema::dropIfExists(config('seo-manager.database.locales_table'));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/migrations/2019_01_12_174747_create_translates_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | $table->integer('route_id');
19 | $table->string('locale');
20 | $table->string('url')->nullable();
21 | $table->jsonb('keywords')->nullable();
22 | $table->string('description')->nullable();
23 | $table->string('title')->nullable();
24 | $table->string('author')->nullable();
25 | $table->jsonb('title_dynamic')->nullable();
26 | $table->jsonb('og_data')->nullable();
27 | $table->timestamps();
28 | });
29 | }
30 |
31 | /**
32 | * Reverse the migrations.
33 | *
34 | * @return void
35 | */
36 | public function down()
37 | {
38 | Schema::dropIfExists(config('seo-manager.database.translates_table'));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/routes/seo-manager.php:
--------------------------------------------------------------------------------
1 | $middleware,
6 | 'prefix' => config('seo-manager.route'),
7 | 'as' => 'seo-manager.',
8 | 'namespace' => 'Lionix\SeoManager\Controllers'
9 | ], function () {
10 | Route::get('/', 'ManagerController@index')->name('home');
11 | Route::get('get-routes', 'ManagerController@getRoutes')->name('get-routes');
12 | Route::get('import-routes', 'ImportController')->name('import');
13 | Route::get('get-models', 'ManagerController@getModels')->name('get-models');
14 |
15 | Route::group(['prefix' => 'locales', 'as' => 'locales.'], function () {
16 | Route::get('get-locales', 'LocalesController@getLocales')->name('get');
17 | });
18 | });
19 | Route::group([
20 | 'middleware' => config('seo-manager.middleware'),
21 | 'prefix' => config('seo-manager.route'),
22 | 'as' => 'seo-manager.',
23 | 'namespace' => 'Lionix\SeoManager\Controllers'
24 | ], function () {
25 | Route::post('delete-route', 'ManagerController@deleteRoute')->name('delete-route');
26 | Route::post('get-model-columns', 'ManagerController@getModelColumns')->name('get-model-columns');
27 | Route::post('store-data', 'ManagerController@storeData')->name('store-data');
28 | Route::post('get-example-title', 'ManagerController@getExampleTitle')->name('get-example-title');
29 | Route::post('sharing-preview', 'ManagerController@sharingPreview')->name('sharing-preview');
30 |
31 | Route::group(['prefix' => 'locales', 'as' => 'locales.'], function () {
32 | Route::post('add-locale', 'LocalesController@addLocale')->name('add');
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/src/views/index.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 | Seo Manager
20 |
21 |
22 |
23 |
24 | {{--Main Content--}}
25 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
34 |
35 |
36 | {{----}}
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/views/og_data.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/vue/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
30 |
31 |
32 |
35 |
--------------------------------------------------------------------------------
/src/vue/components/Header.vue:
--------------------------------------------------------------------------------
1 |
2 |
41 |
42 |
43 |
122 |
123 |
128 |
--------------------------------------------------------------------------------
/src/vue/components/SideBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
25 |
26 |
29 |
--------------------------------------------------------------------------------
/src/vue/components/routes/Routes.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | # |
12 | URI |
13 | Params |
14 | Mapping |
15 | Keywords |
16 | Description |
17 | Title |
18 | URL |
19 | Author |
20 | Title (dynamic) |
21 | Open Graph Data |
22 | Actions |
23 |
24 |
25 |
26 |
27 |
28 | {{ index }}
29 |
30 |
31 |
32 |
33 |
34 |
35 |

37 |
38 |
39 | No routes yet.
40 |
41 |
42 |
43 | Import your all routes by clicking button below
44 |
45 |
46 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
59 |
60 |
62 |
63 |
65 |
66 |
68 |
69 |
71 |
72 |
74 |
75 |
77 |
78 |
80 |
81 |
82 |
83 |
259 |
260 |
263 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/Actions.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
12 | |
13 |
14 |
15 |
59 |
60 |
63 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/Author.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ author }}
6 | -
7 |
8 | |
9 |
10 |
11 |
31 |
32 |
35 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/Description.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ description }}
6 | -
7 |
8 | |
9 |
10 |
11 |
32 |
33 |
36 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/Keywords.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ keyword }}
6 | -
7 |
8 | |
9 |
10 |
11 |
32 |
33 |
38 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/Mapping.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mapped
6 | -
7 |
8 | |
9 |
10 |
11 |
40 |
41 |
44 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/OgData.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Og Data Stored
6 | -
7 |
8 | |
9 |
10 |
11 |
34 |
35 |
38 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/Params.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ param }}
4 | |
5 |
6 |
7 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/Route.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | |
6 | {{ route.uri }} |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
71 |
72 |
75 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/Title.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ title }}
6 | -
7 |
8 | |
9 |
10 |
11 |
31 |
32 |
35 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/TitleDynamic.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 | {{ example_title }}
9 | -
10 |
11 | |
12 |
13 |
14 |
65 |
66 |
69 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/Url.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ url }}
6 | -
7 |
8 | |
9 |
10 |
11 |
31 |
32 |
35 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/mappings/ParamsMapping.vue:
--------------------------------------------------------------------------------
1 |
2 |
37 |
38 |
39 |
88 |
89 |
94 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/mappings/TitleMapping.vue:
--------------------------------------------------------------------------------
1 |
2 |
91 |
92 |
93 |
166 |
167 |
170 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/modals/AuthorModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
39 |
40 |
41 |
81 |
82 |
98 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/modals/DescriptionModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
39 |
40 |
41 |
81 |
82 |
98 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/modals/KeywordsModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
42 |
43 |
44 |
87 |
88 |
104 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/modals/MappingModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
13 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
148 |
149 |
170 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/modals/OgDataModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
121 |
122 |
124 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
445 |
446 |
516 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/modals/PreviewModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 | -
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
![]()
30 |

31 |
32 |
33 |
{{ ogData['og:site_name'] }}
34 |
{{ ogData['og:url'] }}
35 |
{{ ogData['og:title'] }}
36 |
{{ ogData['og:description'] }}
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
91 |
92 |
189 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/modals/TitleDynamicModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
94 |
95 |
96 |
172 |
173 |
193 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/modals/TitleModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
39 |
40 |
41 |
83 |
84 |
100 |
--------------------------------------------------------------------------------
/src/vue/components/routes/partials/modals/UrlModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
39 |
40 |
41 |
81 |
82 |
98 |
--------------------------------------------------------------------------------
/src/vue/event_bus/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 |
3 | export const EventBus = new Vue();
4 |
--------------------------------------------------------------------------------
/src/vue/seo-manager.app.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueResource from 'vue-resource'
3 | import App from './App'
4 | import MultiSelect from 'vue-multiselect'
5 | import VueDraggable from 'vue-draggable'
6 | import ClickOutside from 'vue-click-outside'
7 | import VueSweetalert2 from 'vue-sweetalert2'
8 | import {store} from './store/store'
9 | Vue.use(VueDraggable);
10 | Vue.use(VueResource);
11 | Vue.use(VueSweetalert2);
12 |
13 | const API_URL = window.API_URL;
14 | const CSRF_TOKEN = window.CSRF_TOKEN;
15 |
16 | Vue.filter('uppercase', function (value) {
17 | return value.toUpperCase()
18 | });
19 | Vue.directive('click-outside', ClickOutside);
20 | Vue.component('multi-select', MultiSelect);
21 |
22 | new Vue({
23 | el: '#lionix-seo-manager-app',
24 | store,
25 | components: {
26 | App
27 | }
28 | });
29 |
--------------------------------------------------------------------------------
/src/vue/store/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | Vue.use(Vuex);
5 |
6 | export const store = new Vuex.Store({
7 | state: {
8 | locale: null
9 | },
10 | mutations:{
11 | setLocale(state, locale){
12 | state.locale = locale
13 | }
14 | },
15 | getters: {
16 | locale: state => {
17 | return state.locale
18 | }
19 | }
20 | });
21 |
22 |
--------------------------------------------------------------------------------