Per-User Post Limit & jQuery-Free My Notes
Ringkasan
Video ini membahas dua topik: (1) Membatasi jumlah note yang bisa dibuat per-user menggunakan wp_insert_post_data filter dan count_user_posts(), serta menampilkan jumlah note via REST API field kustom. (2) Menulis ulang fitur My Notes tanpa jQuery menggunakan vanilla JavaScript dan Axios.
BAGIAN 1: Per-User Post Limit
1. Batasi Jumlah Note
Kita ingin membatasi agar setiap user hanya bisa membuat maksimal 5 note.
Update wp_insert_post_data Filter:
add_filter('wp_insert_post_data', 'makeNotePrivate', 10, 2);
function makeNotePrivate($data, $postArray) {
if ($data['post_type'] == 'note') {
// Blokir jika user sudah punya 5+ note DAN ini adalah POST BARU (bukan update)
if (count_user_posts(get_current_user_id(), 'note') > 4 AND !$postArray['ID']) {
die("You have reached your note limit.");
}
if ($data['post_status'] != 'trash') {
$data['post_status'] = 'private';
}
$data['post_title'] = sanitize_text_field($data['post_title']);
$data['post_content'] = sanitize_textarea_field($data['post_content']);
}
return $data;
}Penjelasan Key Concepts:
count_user_posts(user_id, post_type)
- Menghitung jumlah post yang dimiliki user tertentu
- Contoh:
count_user_posts(get_current_user_id(), 'note')→ mengembalikan angka (misal3)
$postArray['ID'] — Membedakan Create vs Update
- Create baru:
$postArray['ID']=0(falsy) →!$postArray['ID']=true - Update existing:
$postArray['ID']=142(truthy) →!$postArray['ID']=false - Ini penting karena kita hanya ingin memblokir pembuatan note baru, bukan update/delete
add_filter('wp_insert_post_data', 'makeNotePrivate', 10, 2)
| Argumen | Nilai | Penjelasan |
|---|---|---|
| 1. Hook name | 'wp_insert_post_data' | Filter hook |
| 2. Callback | 'makeNotePrivate' | Fungsi yang dipanggil |
| 3. Priority | 10 | Urutan eksekusi (default) |
| 4. Accepted args | 2 | Jumlah parameter yang diterima fungsi ($data + $postArray) |
Penting: Argumen ke-4 (
2) wajib karena default-nya adalah1. Tanpa ini, parameter$postArraytidak akan dikirim ke fungsi kita.
2. Expose Note Count via REST API
Agar front-end bisa mengetahui berapa note yang sudah dimiliki user (untuk menyembunyikan form create):
// Di functions.php (dalam hook rest_api_init atau init)
register_rest_field('note', 'userNoteCount', array(
'get_callback' => function() {
return count_user_posts(get_current_user_id(), 'note');
}
));Sekarang setiap response Note dari REST API akan menyertakan field userNoteCount.
3. Front-End: Sembunyikan Form Saat Limit Tercapai
// Di createNote success callback
success: (response) => {
// ... tambahkan note ke DOM ...
// Cek apakah sudah mencapai limit
if (response.userNoteCount >= 5) {
$(".create-note").slideUp();
$(".note-limit-message").slideDown(); // "You have reached your note limit."
}
},
error: (response) => {
// Tampilkan pesan error jika server menolak
$(".note-limit-message").slideDown();
}HTML untuk Error Message:
<p class="note-limit-message" style="display: none;">
You have reached your note limit. Delete an existing note to make room for a new one.
</p>BAGIAN 2: jQuery-Free My Notes
Mengapa Tanpa jQuery?
- jQuery menambah ukuran bundle
- Vanilla JavaScript modern sudah sangat capable
- WordPress bergerak ke arah Gutenberg yang tidak menggunakan jQuery
- Banyak developer lebih suka native API
Pengganti jQuery dengan Vanilla JS + Axios:
| jQuery | Vanilla JS / Axios |
|---|---|
$(selector) | document.querySelector() / document.querySelectorAll() |
$.ajax() | axios() atau axios.delete() / axios.post() |
$(e.target).parents("li") | e.target.closest("li") |
.data("id") | .getAttribute("data-id") atau .dataset.id |
.val() | .value |
.html("...") | .innerHTML = "..." |
.addClass("x") | .classList.add("x") |
.removeClass("x") | .classList.remove("x") |
.slideUp() | Custom CSS animation |
.slideDown() | Custom CSS animation |
$.prependTo("#target") | target.insertAdjacentHTML("afterbegin", html) |
.on("click", ".child", fn) | Event delegation manual |
.removeAttr("readonly") | .removeAttribute("readonly") |
.attr("readonly", "readonly") | .setAttribute("readonly", "readonly") |
Axios Setup
Axios sudah tersedia via @wordpress/scripts atau bisa di-install:
import axios from 'axios';Konfigurasi default header untuk nonce:
axios.defaults.headers.common['X-WP-Nonce'] = universityData.nonce;Dengan ini, kita tidak perlu menambahkan nonce di setiap request!
Delete Note (Axios Version)
async deleteNote(e) {
const thisNote = e.target.closest("li");
try {
const response = await axios.delete(
universityData.root_url + '/wp-json/wp/v2/note/' + thisNote.getAttribute("data-id")
);
thisNote.style.height = thisNote.offsetHeight + 'px';
// Trigger reflow
requestAnimationFrame(() => {
thisNote.classList.add("fade-out");
// Setelah animasi selesai, hapus dari DOM
setTimeout(() => {
thisNote.remove();
}, 401);
});
} catch(e) {
console.log("Sorry, there was a problem.");
}
}Update Note (Axios Version)
async updateNote(e) {
const thisNote = e.target.closest("li");
const updatedData = {
title: thisNote.querySelector(".note-title-field").value,
content: thisNote.querySelector(".note-body-field").value
};
try {
const response = await axios.post(
universityData.root_url + '/wp-json/wp/v2/note/' + thisNote.getAttribute("data-id"),
updatedData
);
this.makeNoteReadOnly(thisNote);
} catch(e) {
console.log("Sorry, could not update.");
}
}Create Note (Axios Version)
async createNote() {
const newPostData = {
title: document.querySelector(".new-note-title").value,
content: document.querySelector(".new-note-body").value,
status: 'publish'
};
try {
const response = await axios.post(
universityData.root_url + '/wp-json/wp/v2/note',
newPostData
);
document.querySelector(".new-note-title").value = '';
document.querySelector(".new-note-body").value = '';
// Tambahkan ke DOM
document.querySelector("#my-notes").insertAdjacentHTML("afterbegin", `
<li data-id="${response.data.id}" class="fade-in-note">
<input readonly class="note-title-field" value="${response.data.title.raw}">
<span class="edit-note"><i class="fa fa-pencil" aria-hidden="true"></i> Edit</span>
<span class="delete-note"><i class="fa fa-trash-o" aria-hidden="true"></i> Delete</span>
<textarea readonly class="note-body-field">${response.data.content.raw}</textarea>
<span class="update-note btn btn--blue btn--small">
<i class="fa fa-arrow-right" aria-hidden="true"></i> Save
</span>
</li>
`);
// Cek limit
if (response.data.userNoteCount >= 5) {
document.querySelector(".create-note").style.display = 'none';
document.querySelector(".note-limit-message").style.display = 'block';
}
} catch(e) {
console.log("Sorry, could not create note.");
}
}Custom Slide Animation (CSS)
Karena tidak ada jQuery .slideUp() / .slideDown(), kita gunakan CSS transitions:
#my-notes li {
overflow: hidden;
transition: opacity 0.4s ease, height 0.4s ease, padding 0.4s ease;
}
#my-notes li.fade-out {
opacity: 0;
height: 0 !important;
padding-top: 0;
padding-bottom: 0;
}
.fade-in-note {
animation: fadeIn 0.4s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}Event Delegation Tanpa jQuery
events() {
document.querySelector("#my-notes").addEventListener("click", (e) => {
// Delete
if (e.target.closest(".delete-note")) {
this.deleteNote(e);
}
// Edit
if (e.target.closest(".edit-note")) {
this.editNote(e);
}
// Update/Save
if (e.target.closest(".update-note")) {
this.updateNote(e);
}
});
document.querySelector(".submit-note").addEventListener("click", this.createNote.bind(this));
}Event delegation di vanilla JS: pasang satu listener di parent, lalu gunakan
e.target.closest(".class")untuk mendeteksi elemen mana yang diklik.
Catatan CSS Tambahan
Instruktur menyebutkan bahwa ada penyesuaian CSS untuk halaman My Notes yang bisa di-copy dari course resources. CSS ini mengatur tampilan:
- Form create note (border, padding, spacing)
- Note list items (card-like appearance)
- Active field highlighting (border biru saat edit mode)
.update-note--visibleclass untuk show/hide tombol Save.note-limit-messagestyling- Animasi fade-out dan fade-in