added table of content [SEO]

pull/49/head
A1Gard 2 months ago
parent 267213cff7
commit 935828dabb

@ -1271,3 +1271,98 @@ function sendingSMS($text, $number, $args)
return true; return true;
} }
/**
* table of content generator
* @param $html
* @return array
*/
function generateTOC($html) {
// Load HTML into a DOMDocument for parsing
$doc = new DOMDocument();
@$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$toc = '';
$tocItems = [];
$lastH2 = '';
$lastH3 = '';
$idCounter = 0;
// Fetch all headings in the document
$headings = $doc->getElementsByTagName('*');
foreach ($headings as $heading) {
if (in_array($heading->nodeName, ['h2', 'h3'])) {
// Generate a unique ID for each heading
$id = generateHeadingID($heading->nodeValue, $idCounter);
$idCounter++;
$heading->setAttribute('id', $id);
if ($heading->nodeName === 'h2') {
$tocItems[] = [
'title' => $heading->nodeValue,
'id' => $id,
'children' => []
];
$lastH2 = $heading->nodeValue; // Update last H2 title
$lastH3 = ''; // Reset last H3
} elseif ($heading->nodeName === 'h3') {
if ($lastH2) {
// Create a new child entry for the last H2
$tocItems[count($tocItems) - 1]['children'][] = [
'title' => $heading->nodeValue,
'id' => $id,
];
$lastH3 = $heading->nodeValue; // Update last H3 title
}
}
}
}
// Create the TOC HTML
$toc .= buildTOC($tocItems);
// Return the modified HTML and the TOC
return [$toc, $doc->saveHTML()];
}
/**
* generate heading ID for table of content
* @param $text
* @param $counter
* @return string
*/
function generateHeadingID($text, $counter) {
// Convert to lowercase and replace non-alphanumeric characters with dashes
$id = strtolower(preg_replace('/[^a-zA-Z0-9]+/', '-', $text));
// Remove leading and trailing dashes
$id = trim($id, '-');
// Ensure the ID is not empty
if (empty($id)) {
$id = 'heading';
}
// Add the counter to ensure uniqueness
$id .= '-' . $counter;
return $id;
}
// The buildTOC function remains unchanged
function buildTOC($items) {
$html = '<ul>';
foreach ($items as $item) {
$html .= '<li>';
$html .= '<a href="#' . $item['id'] . '">' . $item['title'] . '</a>';
if (!empty($item['children'])) {
$html .= buildTOC($item['children']);
}
$html .= '</li>';
}
$html .= '</ul>';
return $html;
}

@ -58,6 +58,7 @@ class PostController extends XController
$post->group_id = $request->input('group_id'); $post->group_id = $request->input('group_id');
$post->user_id = auth()->id(); $post->user_id = auth()->id();
$post->is_pinned = $request->has('is_pin'); $post->is_pinned = $request->has('is_pin');
$post->table_of_contents = $request->has('table_of_contents');
$post->icon = $request->input('icon'); $post->icon = $request->input('icon');
if ($post->hash == null) { if ($post->hash == null) {

@ -196,4 +196,14 @@ RESULT;
return implode(',', $this->tags()->pluck('name')->toArray()); return implode(',', $this->tags()->pluck('name')->toArray());
} }
} }
public function tableOfContents(){
list($toc, $modifiedHtml) = generateTOC($this->body);
return $toc;
}
public function bodyContent(){
list($toc, $modifiedHtml) = generateTOC($this->body);
return $modifiedHtml;
}
} }

@ -6,6 +6,7 @@
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"require": { "require": {
"php": "^8.2", "php": "^8.2",
"ext-dom": "*",
"carlos-meneses/laravel-mpdf": "^2.1", "carlos-meneses/laravel-mpdf": "^2.1",
"chillerlan/php-qrcode": "^5.0", "chillerlan/php-qrcode": "^5.0",
"dpsoft/mellat": "^1.1", "dpsoft/mellat": "^1.1",

@ -26,6 +26,7 @@ return new class extends Migration
$table->unsignedInteger('like')->default(0); $table->unsignedInteger('like')->default(0);
$table->unsignedInteger('dislike')->default(0); $table->unsignedInteger('dislike')->default(0);
$table->string('icon', 128)->nullable(); $table->string('icon', 128)->nullable();
$table->boolean('table_of_contents')->default(0);
$table->softDeletes(); $table->softDeletes();
$table->timestamps(); $table->timestamps();

@ -418,6 +418,7 @@
"Successfully Invoices": "", "Successfully Invoices": "",
"Summary": "خلاصه", "Summary": "خلاصه",
"System notification": "پیام سیستم", "System notification": "پیام سیستم",
"Table of contents": "فهرست عناوین",
"Tag": "برچسب", "Tag": "برچسب",
"Tags": "برچسب‌ها", "Tags": "برچسب‌ها",
"Tags list": "فهرست برچسب‌ها", "Tags list": "فهرست برچسب‌ها",

@ -103,13 +103,23 @@
placeholder="{{__('Title')}}" value="{{old('title',$item->title??null)}}"/> placeholder="{{__('Title')}}" value="{{old('title',$item->title??null)}}"/>
</div> </div>
</div> </div>
<div class="col-md-12 mt-3"> <div class="col-md-9 mt-3">
<label for="slug"> <label for="slug">
{{__('Slug')}} {{__('Slug')}}
</label> </label>
<input name="slug" type="text" class="form-control @error('slug') is-invalid @enderror" <input name="slug" type="text" class="form-control @error('slug') is-invalid @enderror"
placeholder="{{__('Slug')}}" value="{{old('slug',$item->slug??null)}}"/> placeholder="{{__('Slug')}}" value="{{old('slug',$item->slug??null)}}"/>
</div> </div>
<div class="col-3 mt-4">
<div class="form-group mt-4">
<div class="form-check form-switch">
<input class="form-check-input" name="table_of_contents" @if (old('table_of_contents',$item->table_of_contents??0) != 0)
checked
@endif type="checkbox" id="table_of_contents">
<label class="form-check-label" for="table_of_contents">{{__('Table of contents')}}</label>
</div>
</div>
</div>
<div class="col-md-12 mt-3"> <div class="col-md-12 mt-3">
<div class="form-group"> <div class="form-group">
<label for="subtitle"> <label for="subtitle">

@ -44,7 +44,14 @@
@endif @endif
</div> </div>
<hr> <hr>
@if($post->table_of_contents)
{!! $post->tableOfContents() !!}
{!! $post->bodyContent() !!}
@else
{!! $post->body !!} {!! $post->body !!}
@endif
</div> </div>
</div> </div>

@ -37,7 +37,13 @@
@endif @endif
</div> </div>
<hr> <hr>
@if($post->table_of_contents)
{!! $post->tableOfContents() !!}
{!! $post->bodyContent() !!}
@else
{!! $post->body !!} {!! $post->body !!}
@endif
</div> </div>
</div> </div>

Loading…
Cancel
Save