-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlike-button.php
More file actions
159 lines (136 loc) · 5.72 KB
/
Copy pathlike-button.php
File metadata and controls
159 lines (136 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<?php
/**
* Like button for WordPress posts — no plugin, no jQuery.
*
* - The like count is stored in post meta (`like_count`).
* - Duplicate prevention is handled client-side via localStorage.
* - The AJAX handler verifies a nonce and validates the post ID before counting.
*
* Drop the `like-button/` folder into your theme (e.g. `inc/like-button/`) and
* require this file from `functions.php`:
*
* require get_theme_file_path( 'inc/like-button/like-button.php' );
*
* Replace the `yourtheme_` function prefix, the `yourtheme-like` nonce action,
* the `yourthemeLike` JS object and the `yourtheme` text domain with your own
* namespace.
*
* Inspired (among several) by Amr AbdElkarem's tutorial:
* https://medium.com/@amr.abdelkarem/add-an-ajax-like-button-to-wordpress-without-c80834d84b33
*
* @package yourtheme
* @license GPL-2.0-or-later
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* The post meta key that stores the like count.
*
* @return string Meta key.
*/
function yourtheme_like_meta_key() {
return apply_filters( 'yourtheme_like_meta_key', 'like_count' );
}
/**
* Returns the like button HTML for a given post.
*
* Only per-post data (the post ID) sits on the element. Constant data
* (endpoint, nonce, labels) is passed once via wp_localize_script.
*
* @param int $post_id The post ID.
* @return string Button HTML.
*/
function yourtheme_get_like_button( $post_id ) {
$post_id = (int) $post_id;
$like_count = (int) get_post_meta( $post_id, yourtheme_like_meta_key(), true );
$label = esc_attr__( 'Like this post', 'yourtheme' );
// Empty until there is at least one like; capped at "99+" for display.
$count_text = '';
if ( $like_count > 0 ) {
$count_text = esc_html( $like_count > 99 ? '99+' : $like_count );
}
$html = '<button type="button" class="btn-like"';
$html .= ' data-post-id="' . esc_attr( $post_id ) . '"';
$html .= ' aria-label="' . $label . '"';
// Liked state lives in localStorage, so the server cannot know it — JS flips this to true.
$html .= ' aria-pressed="false"';
$html .= '>';
$html .= '<span class="like-icon" aria-hidden="true">';
$html .= '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path></svg>';
$html .= '</span>';
// Visible badge — decorative; the count is announced via the live region below.
$html .= '<span class="like-count" aria-hidden="true">' . $count_text . '</span>';
// Screen-reader live region — JS fills it with a full sentence after a like.
$html .= '<span class="like-status visually-hidden" aria-live="polite"></span>';
$html .= '</button>';
return apply_filters( 'yourtheme_like_button_html', $html, $post_id );
}
/**
* AJAX handler — increments the like count for a post.
*/
function yourtheme_like_post() {
$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
$post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
if ( ! wp_verify_nonce( $nonce, 'yourtheme-like' ) ) {
// wp_send_json_* calls wp_die() internally — no explicit call needed.
wp_send_json_error( 'Invalid nonce' );
}
if ( ! $post_id || ! get_post( $post_id ) ) {
wp_send_json_error( 'Invalid post ID' );
}
$meta_key = yourtheme_like_meta_key();
$like_count = (int) get_post_meta( $post_id, $meta_key, true );
$like_count++;
update_post_meta( $post_id, $meta_key, $like_count );
wp_send_json_success( array( 'count' => $like_count ) );
}
add_action( 'wp_ajax_nopriv_yourtheme_like_post', 'yourtheme_like_post' );
add_action( 'wp_ajax_yourtheme_like_post', 'yourtheme_like_post' );
/**
* Enqueues the like button assets and passes labels + endpoint to JS.
*
* Loads on single posts only — adjust the condition for your use.
*/
function yourtheme_like_enqueue() {
if ( ! is_singular() ) {
return;
}
// Base URL of this folder. Filterable if you place it somewhere other than inc/like-button/.
$base = apply_filters( 'yourtheme_like_asset_base', get_theme_file_uri( 'inc/like-button' ) );
wp_enqueue_style(
'yourtheme-like',
$base . '/like-button.css',
array(),
'1.0.1'
);
wp_enqueue_script(
'yourtheme-like',
$base . '/like-button.js',
array(),
'1.0.1',
array(
'strategy' => 'defer',
'in_footer' => true,
)
);
// Constant, per-page data — passed once, not repeated on every button.
// Singular/plural is chosen client-side because the count is only known there.
wp_localize_script(
'yourtheme-like',
'yourthemeLike',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'action' => 'yourtheme_like_post',
'nonce' => wp_create_nonce( 'yourtheme-like' ),
'labelDefault' => __( 'Like this post', 'yourtheme' ),
'labelAlreadyLiked' => __( 'You have already liked this post', 'yourtheme' ),
/* translators: %s: number of likes (singular). */
'labelLikedSingular' => __( 'Liked. This post now has %s like.', 'yourtheme' ),
/* translators: %s: number of likes (plural). */
'labelLikedPlural' => __( 'Liked. This post now has %s likes.', 'yourtheme' ),
'labelError' => __( 'Unable to like.', 'yourtheme' ),
)
);
}
add_action( 'wp_enqueue_scripts', 'yourtheme_like_enqueue' );