Files
dummylab-wp/dummylab.php
T

563 lines
30 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Plugin Name: DummyLab Dummy Post & Content Generator
* Plugin URI: https://apps.masbudikusuma.my.id/dummylab-wp
* Description: Advanced dummy content generator for WordPress development with safety controls and bulk purge features.
* Version: 1.0.0
* Author: masbudikusuma
* Author URI: https://masbudi.my.id
* Text Domain: dummylab
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
*/
if (!defined('ABSPATH')) {
exit;
}
class DummyLab {
private static $instance = null;
private $meta_key = '_is_dummylab_post';
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_action('admin_menu', [$this, 'add_admin_menu']);
add_action('admin_post_dummylab_generate', [$this, 'handle_generate']);
add_action('admin_post_dummylab_purge', [$this, 'handle_purge']);
}
public function add_admin_menu() {
add_management_page(
'DummyLab Generator',
'DummyLab',
'manage_options',
'dummylab',
[$this, 'render_admin_page']
);
}
public function render_admin_page() {
if (!current_user_can('manage_options')) {
return;
}
$ui_lang = isset($_GET['ui_lang']) && $_GET['ui_lang'] === 'en' ? 'en' : 'id';
$users = get_users(['fields' => ['ID', 'display_name']]);
$query = new WP_Query([
'post_type' => ['post', 'page', 'attachment'],
'posts_per_page' => -1,
'meta_key' => $this->meta_key,
'meta_value' => '1',
'fields' => 'ids',
'post_status' => 'any',
]);
$dummy_count = $query->found_posts;
$t = $this->get_translations($ui_lang);
?>
<div class="wrap">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>
<h1>🧪 DummyLab Post & Page Generator</h1>
<p><?php echo $t['created_by']; ?> <a href="https://masbudi.my.id" target="_blank">masbudikusuma</a> | <a href="https://apps.masbudikusuma.my.id/dummylab-wp" target="_blank"><?php echo $t['plugin_doc']; ?></a></p>
</div>
<!-- UI Language Switcher -->
<div style="background: #fff; border: 1px solid #ccc; padding: 6px 12px; border-radius: 4px;">
<strong>UI Language: </strong>
<a href="<?php echo esc_url(add_query_arg('ui_lang', 'id')); ?>" style="font-weight: <?php echo $ui_lang === 'id' ? 'bold' : 'normal'; ?>;">🇮🇩 ID</a> |
<a href="<?php echo esc_url(add_query_arg('ui_lang', 'en')); ?>" style="font-weight: <?php echo $ui_lang === 'en' ? 'bold' : 'normal'; ?>;">🇬🇧 EN</a>
</div>
</div>
<hr />
<?php if (isset($_GET['status'])): ?>
<div class="notice notice-success is-dismissible">
<p>
<?php
if ($_GET['status'] === 'generated') {
echo esc_html(intval($_GET['count'])) . ' ' . esc_html($t['msg_generated']);
} elseif ($_GET['status'] === 'purged') {
echo esc_html(intval($_GET['count'])) . ' ' . esc_html($t['msg_purged']);
}
?>
</p>
</div>
<?php endif; ?>
<div style="display: flex; gap: 20px; align-items: flex-start; margin-top: 20px;">
<!-- Left Main Form -->
<div class="card" style="flex: 2; min-width: 500px; padding: 20px;">
<h2><?php echo $t['form_title']; ?></h2>
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
<?php wp_nonce_field('dummylab_generate_action', 'dummylab_nonce'); ?>
<input type="hidden" name="action" value="dummylab_generate">
<input type="hidden" name="ui_lang" value="<?php echo esc_attr($ui_lang); ?>">
<table class="form-table">
<tr>
<th scope="row"><label for="post_count"><?php echo $t['lbl_count']; ?></label></th>
<td><input type="number" id="post_count" name="post_count" value="5" min="1" max="50" class="small-text" required></td>
</tr>
<tr>
<th scope="row"><label for="post_type"><?php echo $t['lbl_type']; ?></label></th>
<td>
<select name="post_type" id="post_type">
<option value="post"><?php echo $t['opt_post']; ?></option>
<option value="page"><?php echo $t['opt_page']; ?></option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="content_lang"><?php echo $t['lbl_content_lang']; ?></label></th>
<td>
<select name="content_lang" id="content_lang">
<option value="id">Bahasa Indonesia</option>
<option value="en">English</option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="post_status"><?php echo $t['lbl_status']; ?></label></th>
<td>
<select name="post_status" id="post_status">
<option value="publish"><?php echo $t['opt_publish']; ?></option>
<option value="draft"><?php echo $t['opt_draft']; ?></option>
<option value="pending"><?php echo $t['opt_pending']; ?></option>
<option value="private"><?php echo $t['opt_private']; ?></option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="categories"><?php echo $t['lbl_cats']; ?></label></th>
<td>
<input type="text" name="categories" id="categories" class="regular-text" placeholder="Teknologi, Berita, Hiburan">
<p class="description"><?php echo $t['desc_cats']; ?></p>
</td>
</tr>
<tr>
<th scope="row"><label for="tags"><?php echo $t['lbl_tags']; ?></label></th>
<td>
<input type="text" name="tags" id="tags" class="regular-text" placeholder="wordpress, dummy, test">
<p class="description"><?php echo $t['desc_tags']; ?></p>
</td>
</tr>
<tr>
<th scope="row"><label for="post_author"><?php echo $t['lbl_author']; ?></label></th>
<td>
<select name="post_author" id="post_author">
<option value="random"><?php echo $t['opt_random_author']; ?></option>
<?php foreach ($users as $user): ?>
<option value="<?php echo esc_attr($user->ID); ?>"><?php echo esc_html($user->display_name); ?></option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="date_option"><?php echo $t['lbl_date']; ?></label></th>
<td>
<select name="date_option" id="date_option">
<option value="now"><?php echo $t['opt_date_now']; ?></option>
<option value="random_past"><?php echo $t['opt_date_past']; ?></option>
<option value="future"><?php echo $t['opt_date_future']; ?></option>
</select>
<p class="description"><?php echo $t['desc_date']; ?></p>
</td>
</tr>
<tr>
<th scope="row"><label for="content_length"><?php echo $t['lbl_length']; ?></label></th>
<td>
<select name="content_length" id="content_length">
<option value="short"><?php echo $t['opt_len_short']; ?></option>
<option value="medium" selected><?php echo $t['opt_len_medium']; ?></option>
<option value="long"><?php echo $t['opt_len_long']; ?></option>
</select>
</td>
</tr>
<tr>
<th scope="row"><?php echo $t['lbl_image']; ?></th>
<td>
<label><input type="checkbox" name="use_featured_image" value="1"> <?php echo $t['opt_use_img']; ?></label><br><br>
<select name="image_size" id="image_size">
<option value="800/600"><?php echo $t['opt_img_small']; ?></option>
<option value="1200/800" selected><?php echo $t['opt_img_med']; ?></option>
<option value="1600/1000"><?php echo $t['opt_img_large']; ?></option>
</select>
</td>
</tr>
<tr>
<th scope="row"><?php echo $t['lbl_extra']; ?></th>
<td>
<label><input type="checkbox" name="add_comments" value="1"> <?php echo $t['opt_comments']; ?></label><br>
<label><input type="checkbox" name="make_sticky" value="1"> <?php echo $t['opt_sticky']; ?></label>
</td>
</tr>
</table>
<?php submit_button($t['btn_generate'], 'primary', 'submit'); ?>
</form>
</div>
<!-- Right Sidebar Column -->
<div style="flex: 1; min-width: 300px; display: flex; flex-direction: column; gap: 20px;">
<!-- Rollback Card -->
<div class="card" style="border-left: 4px solid #dc3232; padding: 20px; margin: 0;">
<h2 style="color: #dc3232; margin-top: 0;"><?php echo $t['purge_title']; ?></h2>
<p><?php echo $t['purge_active']; ?>: <strong><?php echo esc_html($dummy_count); ?></strong></p>
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
<?php wp_nonce_field('dummylab_purge_action', 'dummylab_nonce'); ?>
<input type="hidden" name="action" value="dummylab_purge">
<input type="hidden" name="ui_lang" value="<?php echo esc_attr($ui_lang); ?>">
<p class="description"><?php echo $t['purge_desc']; ?></p>
<?php submit_button($t['btn_purge'], 'delete', 'submit', true, ['disabled' => ($dummy_count === 0)]); ?>
</form>
</div>
<!-- How to Use Documentation Card -->
<div class="card" style="padding: 20px; margin: 0;">
<h3 style="margin-top: 0;">📖 <?php echo $t['guide_title']; ?></h3>
<ol style="padding-left: 18px; line-height: 1.6;">
<li><?php echo $t['guide_step1']; ?></li>
<li><?php echo $t['guide_step2']; ?></li>
<li><?php echo $t['guide_step3']; ?></li>
<li><?php echo $t['guide_step4']; ?></li>
</ol>
</div>
<!-- Safety & Features Info Card -->
<div class="card" style="padding: 20px; margin: 0; background-color: #f8f9fa;">
<h3 style="margin-top: 0;">🛡️ <?php echo $t['safety_title']; ?></h3>
<ul style="padding-left: 18px; line-height: 1.6; list-style-type: disc;">
<li><?php echo $t['safety_item1']; ?></li>
<li><?php echo $t['safety_item2']; ?></li>
<li><?php echo $t['safety_item3']; ?></li>
</ul>
</div>
</div>
</div>
</div>
<?php
}
private function get_translations($lang) {
if ($lang === 'en') {
return [
'created_by' => 'Created by',
'plugin_doc' => 'Plugin Documentation',
'msg_generated' => 'dummy posts generated successfully!',
'msg_purged' => 'dummy items deleted successfully!',
'form_title' => 'Generate Dummy Content',
'lbl_count' => 'Post/Page Count',
'lbl_type' => 'Content Type',
'opt_post' => 'Post',
'opt_page' => 'Page',
'lbl_content_lang' => 'Content Language',
'lbl_status' => 'Post Status',
'opt_publish' => 'Publish',
'opt_draft' => 'Draft',
'opt_pending' => 'Pending Review',
'opt_private' => 'Private',
'lbl_cats' => 'Categories',
'desc_cats' => 'Categories created automatically if not present. Comma-separated. Ignored for Pages.',
'lbl_tags' => 'Additional Tags',
'desc_tags' => 'Optional, comma-separated.',
'lbl_author' => 'Author',
'opt_random_author' => 'Random from all users',
'lbl_date' => 'Post Date',
'opt_date_now' => 'Current time',
'opt_date_past' => 'Random in past few days',
'opt_date_future' => 'Future date (Scheduled)',
'desc_date' => 'If future date, post status will automatically set to "Scheduled".',
'lbl_length' => 'Article Length',
'opt_len_short' => 'Short (2-3 paragraphs)',
'opt_len_medium' => 'Medium (4-6 paragraphs + heading)',
'opt_len_long' => 'Long (7-10 paragraphs + heading, quote)',
'lbl_image' => 'Featured Image',
'opt_use_img' => 'Include random image from Picsum Photos',
'opt_img_small' => 'Small (800x600)',
'opt_img_med' => 'Medium (1200x800)',
'opt_img_large' => 'Large (1600x1000)',
'lbl_extra' => 'Additional Options',
'opt_comments' => 'Include dummy comments',
'opt_sticky' => 'Make first post sticky (Post type only)',
'btn_generate' => 'Generate Dummy Data',
'purge_title' => 'Cleanup / Rollback',
'purge_active' => 'Active dummy items',
'purge_desc' => 'Permanently deletes all posts, pages, and media attachments created by DummyLab.',
'btn_purge' => 'Delete All Dummy Data',
'guide_title' => 'Quick Usage Guide',
'guide_step1' => 'Select total count and content type (Post/Page).',
'guide_step2' => 'Choose generated text language and length.',
'guide_step3' => 'Check image box to auto-fetch thumbnails from Picsum.',
'guide_step4' => 'Click Purge anytime to completely clean test data.',
'safety_title' => 'Safe Development',
'safety_item1' => 'All dummy posts are tracked via unique metadata key.',
'safety_item2' => 'Clean purge removes featured images from Media Library.',
'safety_item3' => 'Safe for staging and local theme testing.',
];
}
return [
'created_by' => 'Dibuat oleh',
'plugin_doc' => 'Dokumentasi Plugin',
'msg_generated' => 'konten dummy berhasil dibuat!',
'msg_purged' => 'data dummy berhasil dibersihkan!',
'form_title' => 'Generate Content Dummy',
'lbl_count' => 'Jumlah Post/Halaman',
'lbl_type' => 'Tipe Konten',
'opt_post' => 'Post',
'opt_page' => 'Halaman (Page)',
'lbl_content_lang' => 'Bahasa Konten',
'lbl_status' => 'Status Post',
'opt_publish' => 'Publish (terbit)',
'opt_draft' => 'Draft',
'opt_pending' => 'Pending Review',
'opt_private' => 'Private',
'lbl_cats' => 'Kategori',
'desc_cats' => 'Kategori dibuat otomatis jika belum ada. Pisahkan dengan koma. Tidak berlaku untuk Halaman.',
'lbl_tags' => 'Tag Tambahan',
'desc_tags' => 'Opsional, pisahkan dengan koma.',
'lbl_author' => 'Penulis',
'opt_random_author' => 'Acak dari semua pengguna',
'lbl_date' => 'Tanggal Post',
'opt_date_now' => 'Waktu sekarang',
'opt_date_past' => 'Acak dalam beberapa hari terakhir',
'opt_date_future' => 'Rentang tanggal tertentu (Future/Scheduled)',
'desc_date' => 'Jika tanggal di masa depan, status otomatis "Scheduled".',
'lbl_length' => 'Panjang Artikel',
'opt_len_short' => 'Pendek (2-3 paragraf)',
'opt_len_medium' => 'Sedang (4-6 paragraf + heading)',
'opt_len_long' => 'Panjang (7-10 paragraf + heading, quote)',
'lbl_image' => 'Gambar Unggulan',
'opt_use_img' => 'Sertakan gambar acak dari Picsum Photos',
'opt_img_small' => 'Kecil (800x600)',
'opt_img_med' => 'Sedang (1200x800)',
'opt_img_large' => 'Besar (1600x1000)',
'lbl_extra' => 'Opsi Tambahan',
'opt_comments' => 'Sertakan komentar dummy',
'opt_sticky' => 'Jadikan post pertama sebagai sticky post (khusus Post)',
'btn_generate' => 'Generate Data Dummy',
'purge_title' => 'Cleanup / Rollback',
'purge_active' => 'Total data dummy aktif',
'purge_desc' => 'Tindakan ini akan menghapus permanen seluruh post, page, dan gambar yang dibuat oleh DummyLab.',
'btn_purge' => 'Hapus Semua Data Dummy',
'guide_title' => 'Panduan Penggunaan',
'guide_step1' => 'Pilih jumlah data dan tipe konten (Post atau Halaman).',
'guide_step2' => 'Atur bahasa dan panjang artikel yang akan dihasilkan.',
'guide_step3' => 'Centang opsi gambar untuk mengunduh thumbnail dari Picsum.',
'guide_step4' => 'Gunakan tombol Purge untuk menghapus semua data pengujian.',
'safety_title' => 'Pengembangan Aman',
'safety_item1' => 'Setiap postingan dummy dilindungi metadata internal unik.',
'safety_item2' => 'Fitur Purge juga membersihkan berkas gambar dari Media Library.',
'safety_item3' => 'Aman digunakan pada lingkungan staging maupun lokal.',
];
}
public function handle_generate() {
if (!current_user_can('manage_options') || !check_admin_referer('dummylab_generate_action', 'dummylab_nonce')) {
wp_die('Akses ditolak.');
}
require_once(ABSPATH . 'wp-admin/includes/image.php');
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/media.php');
$ui_lang = isset($_POST['ui_lang']) ? sanitize_text_field($_POST['ui_lang']) : 'id';
$count = isset($_POST['post_count']) ? min(50, max(1, intval($_POST['post_count']))) : 5;
$post_type = isset($_POST['post_type']) && in_array($_POST['post_type'], ['post', 'page']) ? $_POST['post_type'] : 'post';
$lang = isset($_POST['content_lang']) ? sanitize_text_field($_POST['content_lang']) : 'id';
$status = isset($_POST['post_status']) ? sanitize_text_field($_POST['post_status']) : 'publish';
$categories_raw = isset($_POST['categories']) ? sanitize_text_field($_POST['categories']) : '';
$tags_raw = isset($_POST['tags']) ? sanitize_text_field($_POST['tags']) : '';
$author_opt = isset($_POST['post_author']) ? sanitize_text_field($_POST['post_author']) : 'random';
$date_opt = isset($_POST['date_option']) ? sanitize_text_field($_POST['date_option']) : 'now';
$content_length = isset($_POST['content_length']) ? sanitize_text_field($_POST['content_length']) : 'medium';
$use_image = isset($_POST['use_featured_image']) ? true : false;
$image_size = isset($_POST['image_size']) ? sanitize_text_field($_POST['image_size']) : '1200/800';
$add_comments = isset($_POST['add_comments']) ? true : false;
$make_sticky = isset($_POST['make_sticky']) ? true : false;
$cat_ids = [];
if ($post_type === 'post' && !empty($categories_raw)) {
$cat_names = array_map('trim', explode(',', $categories_raw));
foreach ($cat_names as $cat_name) {
if (empty($cat_name)) continue;
$term = term_exists($cat_name, 'category');
if (!$term) {
$term = wp_insert_term($cat_name, 'category');
}
if (!is_wp_error($term)) {
$cat_ids[] = (int) $term['term_id'];
}
}
}
$tag_names = !empty($tags_raw) ? array_map('trim', explode(',', $tags_raw)) : [];
$user_ids = get_users(['fields' => 'ID']);
for ($i = 0; $i < $count; $i++) {
$author_id = ($author_opt === 'random') ? $user_ids[array_rand($user_ids)] : intval($author_opt);
$post_date = current_time('mysql');
$final_status = $status;
if ($date_opt === 'random_past') {
$days_back = rand(1, 30);
$post_date = date('Y-m-d H:i:s', strtotime("-$days_back days"));
} elseif ($date_opt === 'future') {
$days_ahead = rand(1, 15);
$post_date = date('Y-m-d H:i:s', strtotime("+$days_ahead days"));
$final_status = 'future';
}
$title = $this->get_sample_title($lang, $i);
$content = $this->get_sample_content($lang, $content_length);
$post_data = [
'post_title' => $title,
'post_content' => $content,
'post_status' => $final_status,
'post_type' => $post_type,
'post_author' => $author_id,
'post_date' => $post_date,
];
if ($post_type === 'post' && !empty($cat_ids)) {
$post_data['post_category'] = [$cat_ids[array_rand($cat_ids)]];
}
if ($post_type === 'post' && !empty($tag_names)) {
$post_data['tags_input'] = $tag_names;
}
$post_id = wp_insert_post($post_data);
if ($post_id && !is_wp_error($post_id)) {
update_post_meta($post_id, $this->meta_key, '1');
if ($use_image) {
$image_url = 'https://picsum.photos/' . $image_size . '?random=' . rand(1, 9999);
$tmp = download_url($image_url);
if (!is_wp_error($tmp)) {
$file_array = [
'name' => 'dummylab-' . $post_id . '.jpg',
'tmp_name' => $tmp
];
$attach_id = media_handle_sideload($file_array, $post_id);
if (!is_wp_error($attach_id)) {
set_post_thumbnail($post_id, $attach_id);
update_post_meta($attach_id, $this->meta_key, '1');
}
@unlink($tmp);
}
}
if ($i === 0 && $make_sticky && $post_type === 'post') {
stick_post($post_id);
}
if ($add_comments) {
$comment_count = rand(1, 3);
for ($c = 0; $c < $comment_count; $c++) {
wp_insert_comment([
'comment_post_ID' => $post_id,
'comment_author' => 'Tester Dummy',
'comment_author_email' => '[email protected]',
'comment_content' => ($lang === 'id') ? 'Artikel yang sangat bermanfaat dan informatif!' : 'Great content, thanks for sharing!',
'comment_approved' => 1,
]);
}
}
}
}
wp_redirect(admin_url('tools.php?page=dummylab&ui_lang=' . $ui_lang . '&status=generated&count=' . $count));
exit;
}
public function handle_purge() {
if (!current_user_can('manage_options') || !check_admin_referer('dummylab_purge_action', 'dummylab_nonce')) {
wp_die('Akses ditolak.');
}
$ui_lang = isset($_POST['ui_lang']) ? sanitize_text_field($_POST['ui_lang']) : 'id';
$query = new WP_Query([
'post_type' => ['post', 'page', 'attachment'],
'posts_per_page' => -1,
'meta_key' => $this->meta_key,
'meta_value' => '1',
'fields' => 'ids',
'post_status' => 'any',
]);
$purged_count = 0;
if (!empty($query->posts)) {
foreach ($query->posts as $post_id) {
if (wp_delete_post($post_id, true)) {
$purged_count++;
}
}
}
wp_redirect(admin_url('tools.php?page=dummylab&ui_lang=' . $ui_lang . '&status=purged&count=' . $purged_count));
exit;
}
private function get_sample_title($lang, $index) {
$titles_id = [
'Panduan Lengkap Pengembangan Infrastruktur Web Modern',
'Mengoptimalkan Performa Database untuk Trafik Tinggi',
'Desain Responsif dan Pengalaman Pengguna Terbaik',
'Strategi Keamanan Server dan Arsitektur Komputasi Awan',
'Manajemen Konten Efektif untuk Pertumbuhan Bisnis'
];
$titles_en = [
'Exploring the Future of Scalable Web Architecture',
'10 Essential Practices for High-Performance Servers',
'Understanding Modern UI/UX Design Principles',
'Building Secure Application Infrastructures',
'Optimizing Database Queries for Enterprise Solutions'
];
$arr = ($lang === 'id') ? $titles_id : $titles_en;
return $arr[array_rand($arr)] . ' #' . rand(100, 999);
}
private function get_sample_content($lang, $length) {
$p_id = [
'Pengembangan aplikasi web modern memerlukan perhatian ekstra pada arsitektur sistem dasar. Komponen seperti manajemen memori, optimasi kueri, dan konfigurasi server menjadi kunci utama.',
'Dengan menerapkan standar keamanan terbaik, kita dapat mencegah potensi bahaya siber dan memastikan layanan tetap stabil di bawah beban lalu lintas data yang tinggi.',
'Pengalaman pengguna merupakan elemen krusial. Tata letak visual yang konsisten serta navigasi intuitif memudahkan pengunjung dalam menemukan informasi secara cepat.'
];
$p_en = [
'Modern web applications require strict adherence to solid system architectures. Key aspects include efficient database queries, memory management, and caching layer implementation.',
'Ensuring continuous infrastructure security prevents vulnerabilities while guaranteeing optimal uptime during high-volume traffic spikes.',
'User experience design directly impacts engagement rates. Clean layouts, readable typography, and intuitive interfaces make content consumption seamless.'
];
$paragraphs = ($lang === 'id') ? $p_id : $p_en;
$output = '';
$loops = ($length === 'short') ? 2 : (($length === 'long') ? 7 : 4);
for ($k = 0; $k < $loops; $k++) {
$output .= '<p>' . $paragraphs[array_rand($paragraphs)] . '</p>';
if ($length !== 'short' && $k === 1) {
$output .= '<h2>' . (($lang === 'id') ? 'Sub-judul Informasi Penting' : 'Key Technical Insights') . '</h2>';
}
if ($length === 'long' && $k === 3) {
$output .= '<blockquote><p>' . (($lang === 'id') ? 'Keamanan dan kecepatan adalah fondasi utama pengembangan web.' : 'Security and performance are the core foundations of web development.') . '</p></blockquote>';
}
}
return $output;
}
}
DummyLab::get_instance();