nextpress-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/acf-functions/core/acf-builder.ts +105 -0
- package/lib/acf-functions/core/acf-component-autoloader.ts +33 -0
- package/lib/acf-functions/core/acf-field-group-autoloader.ts +28 -0
- package/lib/acf-functions/services/define-field-group.ts +10 -0
- package/lib/acf-functions/services/define-layout.ts +10 -0
- package/lib/acf-functions/services/map-fields/helpers/map-choice-object.ts +20 -0
- package/lib/acf-functions/services/map-fields/map-fields.ts +359 -0
- package/lib/acf-functions/types/acf-field-group.d.ts +118 -0
- package/lib/acf-functions/types/acf-field.d.ts +2851 -0
- package/lib/acf-functions/types/acf-layout.d.ts +20 -0
- package/lib/acf-functions/types/acf-values.d.ts +4 -0
- package/lib/acf-functions/types/components/field-props.d.ts +189 -0
- package/lib/acf-functions/types/components/nextpress-component.d.ts +11 -0
- package/lib/ambient.d.ts +15 -0
- package/lib/entities/common.d.ts +30 -0
- package/lib/entities/option/option.interface.d.ts +8 -0
- package/lib/entities/option/option.ts +43 -0
- package/lib/entities/post/post.interface.d.ts +72 -0
- package/lib/entities/post/post.ts +209 -0
- package/lib/entities/term/term.interface.d.ts +8 -0
- package/lib/entities/term/term.ts +82 -0
- package/lib/entities/user/user.interface.d.ts +10 -0
- package/lib/entities/user/user.ts +86 -0
- package/lib/globals/entity-loader/entity-loader-base.ts +141 -0
- package/lib/globals/entity-loader/entity-loader.d.ts +50 -0
- package/lib/globals/entity-loader/option-loader.ts +56 -0
- package/lib/globals/entity-loader/post-loader.ts +59 -0
- package/lib/globals/entity-loader/term-loader.ts +59 -0
- package/lib/globals/entity-loader/user-loader.ts +60 -0
- package/lib/globals/get-field/get-field.ts +75 -0
- package/lib/globals/globals.ts +25 -0
- package/lib/globals/nextpress-config/nextpress-config.interface.d.ts +18 -0
- package/lib/globals/nextpress-config/nextpress-config.ts +7 -0
- package/lib/globals/queried-object/queried-object.ts +124 -0
- package/lib/repository/optionquery/option-query-args.d.ts +21 -0
- package/lib/repository/optionquery/option-query.ts +29 -0
- package/lib/repository/postquery/post-query-args.d.ts +108 -0
- package/lib/repository/postquery/post-query.ts +281 -0
- package/lib/repository/termquery/term-query-args.d.ts +61 -0
- package/lib/repository/termquery/term-query.ts +243 -0
- package/lib/repository/userquery/user-query-args.d.ts +75 -0
- package/lib/repository/userquery/user-query.ts +195 -0
- package/lib/router/helpers.ts +20 -0
- package/lib/router/nextpress-layout.tsx +48 -0
- package/lib/router/nextpress-proxy.ts +45 -0
- package/lib/router/nextpress-static-params.ts +62 -0
- package/lib/router/router.tsx +61 -0
- package/lib/router/routes/api/api-get-admin-bar.ts +29 -0
- package/lib/router/routes/api/api-get-draft-mode.ts +47 -0
- package/lib/router/routes/api/api-get-field-groups.ts +29 -0
- package/lib/router/routes/api/api-post-revalidate.ts +30 -0
- package/lib/router/routes/api/helpers.ts +17 -0
- package/lib/router/routes/author-archive.tsx +75 -0
- package/lib/router/routes/not-found-page.tsx +22 -0
- package/lib/router/routes/post-index-page.tsx +58 -0
- package/lib/router/routes/singular-page.tsx +78 -0
- package/lib/router/routes/site-front-page.tsx +51 -0
- package/lib/router/routes/term-archive.tsx +93 -0
- package/lib/router/types.d.ts +9 -0
- package/lib/services/get-menu.ts +104 -0
- package/lib/services/get-theme-mods.ts +16 -0
- package/lib/services/metadata/get-blogname.ts +9 -0
- package/lib/services/metadata/get-favicon-url.ts +13 -0
- package/lib/services/metadata/get-language-attribute.ts +9 -0
- package/lib/services/services.ts +7 -0
- package/lib/services/utilities/capitalise-first-letter.ts +10 -0
- package/lib/services/utilities/esc-html.ts +12 -0
- package/lib/services/utilities/get-date-time-formatter.ts +49 -0
- package/lib/services/utilities/index.ts +0 -0
- package/lib/services/utilities/kses-post.ts +13 -0
- package/lib/services/utilities/process-url.ts +15 -0
- package/lib/template-heirarchy/_autoloader/template-autoloader.ts +54 -0
- package/lib/template-heirarchy/archive/archive.tsx +28 -0
- package/lib/template-heirarchy/archive/author.tsx +28 -0
- package/lib/template-heirarchy/archive/category.tsx +29 -0
- package/lib/template-heirarchy/archive/posttypearchive.tsx +33 -0
- package/lib/template-heirarchy/archive/tag.tsx +30 -0
- package/lib/template-heirarchy/archive/taxonomy.tsx +28 -0
- package/lib/template-heirarchy/home/home.tsx +28 -0
- package/lib/template-heirarchy/index.tsx +30 -0
- package/lib/template-heirarchy/not-found.tsx/not-found.tsx +28 -0
- package/lib/template-heirarchy/page/page.tsx +28 -0
- package/lib/template-heirarchy/page/posttype.tsx +34 -0
- package/lib/template-heirarchy/page/single.tsx +28 -0
- package/lib/template-heirarchy/page/singular.tsx +28 -0
- package/lib/ui/render-attachment-image.tsx +29 -0
- package/lib/ui/render-components.tsx +12 -0
- package/lib/ui/render-the-admin-bar.tsx +79 -0
- package/lib/ui/render-the-logo.tsx +24 -0
- package/lib/wpdb/wpdb.interface.d.ts +171 -0
- package/lib/wpdb/wpdb.ts +36 -0
- package/package.json +38 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the arguments for querying terms.
|
|
3
|
+
* Maps standard WordPress WP_Term_Query parameters.
|
|
4
|
+
*/
|
|
5
|
+
interface TermQueryArgs {
|
|
6
|
+
/** Taxonomy name, or array of taxonomy names, to which results should be limited. */
|
|
7
|
+
taxonomy?: string | string[];
|
|
8
|
+
/** Taxonomy ID, or array of term taxonomy IDs, to match when querying terms. */
|
|
9
|
+
taxonomyId?: number | number[];
|
|
10
|
+
/** TermId or array of term IDs to include along with all of their descendant terms. */
|
|
11
|
+
termId?: number | number[];
|
|
12
|
+
/** TermId or array of term IDs to exclude along with all of their descendant terms. */
|
|
13
|
+
termIdNot?: number | number[];
|
|
14
|
+
/** Slug or array of slugs to return term(s) for. Default empty. */
|
|
15
|
+
termSlug?: string | string[];
|
|
16
|
+
/** Slug or array of slugs to return term(s) for. Default empty. */
|
|
17
|
+
termSlugNot?: string | string[];
|
|
18
|
+
/** Name or array of names to return term(s) for. Default empty. */
|
|
19
|
+
termName?: string | string[];
|
|
20
|
+
/** Nextpress path to the term */
|
|
21
|
+
path?: string;
|
|
22
|
+
/** Whether to hide terms not assigned to any posts. Default true. */
|
|
23
|
+
hideEmpty?: boolean;
|
|
24
|
+
/** Field(s) to order terms by. Default 'none'. */
|
|
25
|
+
orderBy?:
|
|
26
|
+
| 'term_id'
|
|
27
|
+
| 'name'
|
|
28
|
+
| 'slug'
|
|
29
|
+
| 'term_group'
|
|
30
|
+
| 'description'
|
|
31
|
+
| 'parent'
|
|
32
|
+
| 'term_order'
|
|
33
|
+
| 'count'
|
|
34
|
+
| 'none'
|
|
35
|
+
| string;
|
|
36
|
+
/** Whether to order terms in ascending or descending order. Default 'ASC'. */
|
|
37
|
+
order?: 'ASC' | 'DESC';
|
|
38
|
+
/** Maximum number of terms to return. Accepts 0 (all) or any positive number. Default 0 (all). */
|
|
39
|
+
number?: number;
|
|
40
|
+
/** The number by which to offset the terms query. Default empty. */
|
|
41
|
+
offset?: number;
|
|
42
|
+
/** Parent term ID to retrieve direct-child terms of. Default empty. */
|
|
43
|
+
parent?: number;
|
|
44
|
+
/** True to limit results to terms that have no children. This parameter has no effect on non-hierarchical taxonomies. Default false. */
|
|
45
|
+
childless?: boolean;
|
|
46
|
+
/** Search criteria to match terms. Will be SQL-formatted with wildcards before and after. Default empty. */
|
|
47
|
+
search?: string;
|
|
48
|
+
/** Retrieve terms with criteria by which a term is LIKE `$nameLike`. Default empty. */
|
|
49
|
+
nameLike?: string;
|
|
50
|
+
/** Retrieve terms where the description is LIKE `$descriptionLike`. Default empty. */
|
|
51
|
+
descriptionLike?: string;
|
|
52
|
+
/** Meta query */
|
|
53
|
+
metaQuery?: {
|
|
54
|
+
/** Meta key */
|
|
55
|
+
metaKey: string,
|
|
56
|
+
/** Variable name to save result to */
|
|
57
|
+
as: string
|
|
58
|
+
}[]
|
|
59
|
+
/** Whether to get multiple, default true */
|
|
60
|
+
multiple?: bool
|
|
61
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { QueryCreator } from "kysely";
|
|
2
|
+
import { DB } from "../../wpdb/wpdb.interface";
|
|
3
|
+
import { EntityQuery } from "../../globals/entity-loader/entity-loader";
|
|
4
|
+
import { ITerm } from "../../entities/term/term.interface";
|
|
5
|
+
import { wpdb } from "@/wpdb/wpdb";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Executes database queries to retrieve term IDs based on provided arguments.
|
|
9
|
+
*/
|
|
10
|
+
export class TermQuery implements EntityQuery<ITerm>
|
|
11
|
+
{
|
|
12
|
+
public constructor(
|
|
13
|
+
private args: TermQueryArgs
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
public getCount() {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public async getIds(): Promise<number[]> {
|
|
21
|
+
let builder = wpdb as QueryCreator<any>;
|
|
22
|
+
|
|
23
|
+
const isMultiple = this.args.multiple !== false;
|
|
24
|
+
|
|
25
|
+
// -- Build CTEs --
|
|
26
|
+
if (this.args.termId) {
|
|
27
|
+
const termIds = Array.isArray(this.args.termId) ? this.args.termId : [this.args.termId];
|
|
28
|
+
const baseQuery = (qb: any) => qb.selectFrom('wpTermTaxonomy')
|
|
29
|
+
.select(['termId', 'parent'])
|
|
30
|
+
.where((eb: any) => eb.or([
|
|
31
|
+
eb('termId', 'in', termIds),
|
|
32
|
+
eb('parent', 'in', termIds)
|
|
33
|
+
]));
|
|
34
|
+
|
|
35
|
+
if (isMultiple) {
|
|
36
|
+
builder = builder.withRecursive('included_branch_id', (qb) =>
|
|
37
|
+
baseQuery(qb).unionAll(
|
|
38
|
+
qb.selectFrom('wpTermTaxonomy as t')
|
|
39
|
+
.select(['t.termId', 't.parent'])
|
|
40
|
+
.innerJoin('included_branch_id as f', 'f.termId', 't.parent')
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
} else {
|
|
44
|
+
builder = builder.with('included_branch_id', baseQuery);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (this.args.termIdNot) {
|
|
49
|
+
const termIds = Array.isArray(this.args.termIdNot) ? this.args.termIdNot : [this.args.termIdNot];
|
|
50
|
+
const baseQuery = (qb: any) => qb.selectFrom('wpTermTaxonomy')
|
|
51
|
+
.select(['termId', 'parent'])
|
|
52
|
+
.where((eb: any) => eb.or([
|
|
53
|
+
eb('termId', 'in', termIds),
|
|
54
|
+
eb('parent', 'in', termIds)
|
|
55
|
+
]));
|
|
56
|
+
|
|
57
|
+
if (isMultiple) {
|
|
58
|
+
builder = builder.withRecursive('excluded_branch_id', (qb) =>
|
|
59
|
+
baseQuery(qb).unionAll(
|
|
60
|
+
qb.selectFrom('wpTermTaxonomy as t')
|
|
61
|
+
.select(['t.termId', 't.parent'])
|
|
62
|
+
.innerJoin('excluded_branch_id as f', 'f.termId', 't.parent')
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
} else {
|
|
66
|
+
builder = builder.with('excluded_branch_id', baseQuery);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this.args.termName) {
|
|
71
|
+
const names = Array.isArray(this.args.termName) ? this.args.termName : [this.args.termName];
|
|
72
|
+
const baseQuery = (qb: any) => qb.selectFrom('wpTermTaxonomy')
|
|
73
|
+
.innerJoin('wpTerms', 'wpTerms.termId', 'wpTermTaxonomy.termId')
|
|
74
|
+
.select(['wpTermTaxonomy.termId', 'wpTermTaxonomy.parent'])
|
|
75
|
+
.where('wpTerms.name', 'in', names);
|
|
76
|
+
|
|
77
|
+
if (isMultiple) {
|
|
78
|
+
builder = builder.withRecursive('included_branch_name', (qb) =>
|
|
79
|
+
baseQuery(qb).unionAll(
|
|
80
|
+
qb.selectFrom('wpTermTaxonomy as t')
|
|
81
|
+
.select(['t.termId', 't.parent'])
|
|
82
|
+
.innerJoin('included_branch_name as f', 'f.termId', 't.parent')
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
} else {
|
|
86
|
+
builder = builder.with('included_branch_name', baseQuery);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (this.args.termSlug) {
|
|
91
|
+
const slugs = Array.isArray(this.args.termSlug) ? this.args.termSlug : [this.args.termSlug];
|
|
92
|
+
const baseQuery = (qb: any) => qb.selectFrom('wpTermTaxonomy')
|
|
93
|
+
.innerJoin('wpTerms', 'wpTerms.termId', 'wpTermTaxonomy.termId')
|
|
94
|
+
.select(['wpTermTaxonomy.termId', 'wpTermTaxonomy.parent'])
|
|
95
|
+
.where('wpTerms.slug', 'in', slugs);
|
|
96
|
+
|
|
97
|
+
if (isMultiple) {
|
|
98
|
+
builder = builder.withRecursive('included_branch_slug', (qb) =>
|
|
99
|
+
baseQuery(qb).unionAll(
|
|
100
|
+
qb.selectFrom('wpTermTaxonomy as t')
|
|
101
|
+
.select(['t.termId', 't.parent'])
|
|
102
|
+
.innerJoin('included_branch_slug as f', 'f.termId', 't.parent')
|
|
103
|
+
)
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
builder = builder.with('included_branch_slug', baseQuery);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (this.args.termSlugNot) {
|
|
111
|
+
const slugs = Array.isArray(this.args.termSlugNot) ? this.args.termSlugNot : [this.args.termSlugNot];
|
|
112
|
+
const baseQuery = (qb: any) => qb.selectFrom('wpTermTaxonomy')
|
|
113
|
+
.innerJoin('wpTerms', 'wpTerms.termId', 'wpTermTaxonomy.termId')
|
|
114
|
+
.select(['wpTermTaxonomy.termId', 'wpTermTaxonomy.parent'])
|
|
115
|
+
.where('wpTerms.slug', 'in', slugs);
|
|
116
|
+
|
|
117
|
+
if (isMultiple) {
|
|
118
|
+
builder = builder.withRecursive('excluded_branch_slug', (qb) =>
|
|
119
|
+
baseQuery(qb).unionAll(
|
|
120
|
+
qb.selectFrom('wpTermTaxonomy as t')
|
|
121
|
+
.select(['t.termId', 't.parent'])
|
|
122
|
+
.innerJoin('excluded_branch_slug as f', 'f.termId', 't.parent')
|
|
123
|
+
)
|
|
124
|
+
);
|
|
125
|
+
} else {
|
|
126
|
+
builder = builder.with('excluded_branch_slug', baseQuery);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// -- Begin Main SELECT Statement --
|
|
131
|
+
let query = (builder as QueryCreator<DB>).selectFrom('wpTerms')
|
|
132
|
+
.innerJoin('wpTermTaxonomy', 'wpTerms.termId', 'wpTermTaxonomy.termId');
|
|
133
|
+
|
|
134
|
+
// -- Apply Filters (WHERE clauses) --
|
|
135
|
+
if (this.args.taxonomy) {
|
|
136
|
+
const taxonomies = Array.isArray(this.args.taxonomy) ? this.args.taxonomy : [this.args.taxonomy];
|
|
137
|
+
query = query.where('wpTermTaxonomy.taxonomy', 'in', taxonomies);
|
|
138
|
+
}
|
|
139
|
+
if (this.args.taxonomyId) {
|
|
140
|
+
const ids = Array.isArray(this.args.taxonomyId) ? this.args.taxonomyId : [this.args.taxonomyId];
|
|
141
|
+
query = query.where('wpTermTaxonomy.termTaxonomyId', 'in', ids);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (this.args.termId) query = query.where('wpTerms.termId', 'in', (qb: any) => qb.selectFrom('included_branch_id').select('termId'));
|
|
145
|
+
if (this.args.termIdNot) query = query.where('wpTerms.termId', 'not in', (qb: any) => qb.selectFrom('excluded_branch_id').select('termId'));
|
|
146
|
+
if (this.args.termName) query = query.where('wpTerms.termId', 'in', (qb: any) => qb.selectFrom('included_branch_name').select('termId'));
|
|
147
|
+
if (this.args.termSlug) query = query.where('wpTerms.termId', 'in', (qb: any) => qb.selectFrom('included_branch_slug').select('termId'));
|
|
148
|
+
if (this.args.termSlugNot) query = query.where('wpTerms.termId', 'not in', (qb: any) => qb.selectFrom('excluded_branch_slug').select('termId'));
|
|
149
|
+
if (this.args.hideEmpty !== false) query = query.where('wpTermTaxonomy.count', '>', 0);
|
|
150
|
+
|
|
151
|
+
if (this.args.parent !== undefined) {
|
|
152
|
+
query = query.where('wpTermTaxonomy.parent', '=', this.args.parent);
|
|
153
|
+
}
|
|
154
|
+
if (this.args.childless) {
|
|
155
|
+
query = query.where('wpTerms.termId', 'not in', (qb) =>
|
|
156
|
+
qb.selectFrom('wpTermTaxonomy').select('wpTermTaxonomy.parent').where('wpTermTaxonomy.parent', '>', 0)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (this.args.search) {
|
|
161
|
+
const searchStr = `%${this.args.search}%`;
|
|
162
|
+
query = query.where((eb) => eb.or([
|
|
163
|
+
eb('wpTerms.name', 'like', searchStr),
|
|
164
|
+
eb('wpTerms.slug', 'like', searchStr)
|
|
165
|
+
]));
|
|
166
|
+
}
|
|
167
|
+
if (this.args.nameLike) {
|
|
168
|
+
query = query.where('wpTerms.name', 'like', this.args.nameLike);
|
|
169
|
+
}
|
|
170
|
+
if (this.args.descriptionLike) {
|
|
171
|
+
query = query.where('wpTermTaxonomy.description', 'like', this.args.descriptionLike);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (this.args.path) {
|
|
175
|
+
query = query.where((eb) => eb.exists(
|
|
176
|
+
eb.selectFrom('wpTermmeta')
|
|
177
|
+
.select('wpTerms.termId')
|
|
178
|
+
.whereRef('wpTermmeta.termId', '=', 'wpTerms.termId')
|
|
179
|
+
.where('wpTermmeta.metaKey', '=', '_nextpress_path')
|
|
180
|
+
.where('wpTermmeta.metaValue', '=', this.args.path as string)
|
|
181
|
+
));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// -- Apply ORDER BY --
|
|
185
|
+
const orderDirection = this.args.order === 'DESC' ? 'desc' : 'asc';
|
|
186
|
+
const orderBy = this.args.orderBy || 'none';
|
|
187
|
+
|
|
188
|
+
switch(orderBy) {
|
|
189
|
+
case 'none': break;
|
|
190
|
+
case 'term_id': query = query.orderBy('wpTerms.termId', orderDirection); break;
|
|
191
|
+
case 'name': query = query.orderBy('wpTerms.name', orderDirection).orderBy('wpTerms.termId', orderDirection); break;
|
|
192
|
+
case 'slug': query = query.orderBy('wpTerms.slug', orderDirection).orderBy('wpTerms.termId', orderDirection); break;
|
|
193
|
+
case 'term_group': query = query.orderBy('wpTerms.termGroup', orderDirection).orderBy('wpTerms.termId', orderDirection); break;
|
|
194
|
+
case 'description': query = query.orderBy('wpTermTaxonomy.description', orderDirection).orderBy('wpTerms.termId', orderDirection); break;
|
|
195
|
+
case 'parent': query = query.orderBy('wpTermTaxonomy.parent', orderDirection).orderBy('wpTerms.termId', orderDirection); break;
|
|
196
|
+
case 'count': query = query.orderBy('wpTermTaxonomy.count', orderDirection).orderBy('wpTerms.termId', orderDirection); break;
|
|
197
|
+
default: query = query.orderBy('wpTerms.name', orderDirection).orderBy('wpTerms.termId', orderDirection); break;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// -- Apply LIMIT & OFFSET --
|
|
201
|
+
const limit = this.args.number ?? 0;
|
|
202
|
+
if (limit > 0) {
|
|
203
|
+
query = query.limit(limit);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const offsetAmount = this.args.offset ?? 0;
|
|
207
|
+
if (offsetAmount > 0) {
|
|
208
|
+
query = query.offset(offsetAmount);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (this.args.metaQuery) {
|
|
212
|
+
let dynamicQuery: any = query;
|
|
213
|
+
|
|
214
|
+
for (let metaQuery of this.args.metaQuery) {
|
|
215
|
+
const uniqueAlias = `meta_${metaQuery.as}`;
|
|
216
|
+
|
|
217
|
+
dynamicQuery = dynamicQuery.leftJoin(`wpTermmeta as ${uniqueAlias}`, (join: any) =>
|
|
218
|
+
join
|
|
219
|
+
.onRef(`${uniqueAlias}.termId`, '=', 'wpTerms.termId')
|
|
220
|
+
.on(`${uniqueAlias}.metaKey`, '=', metaQuery.metaKey)
|
|
221
|
+
)
|
|
222
|
+
.select(`${uniqueAlias}.metaValue as ${metaQuery.as}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
query = dynamicQuery;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
if (isMultiple) {
|
|
230
|
+
return (await query
|
|
231
|
+
.select('wpTerms.termId')
|
|
232
|
+
.groupBy('wpTerms.termId')
|
|
233
|
+
.execute()).map((res: any) => res.termId);
|
|
234
|
+
} else {
|
|
235
|
+
return (await query
|
|
236
|
+
.select('wpTerms.termId')
|
|
237
|
+
.execute()).map((res: any) => res.termId);
|
|
238
|
+
}
|
|
239
|
+
} catch (error: any) {
|
|
240
|
+
throw new Error(`WPTermQuery: Cannot get terms: ${error.message}`, { cause: error });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the arguments for querying users.
|
|
3
|
+
* Maps standard WordPress WP_User_Query parameters.
|
|
4
|
+
*/
|
|
5
|
+
interface UserQueryArgs {
|
|
6
|
+
/** User ID, or array of user IDs, to match when querying terms. */
|
|
7
|
+
userId?: number | number[];
|
|
8
|
+
/** An array of user IDs to exclude. Default empty array. */
|
|
9
|
+
userIdsNotIn?: number[];
|
|
10
|
+
/** The user nicename. Default empty. */
|
|
11
|
+
nicename?: string;
|
|
12
|
+
/** An array of nicenames to include. Default empty array. */
|
|
13
|
+
nicenameIn?: string[];
|
|
14
|
+
/** An array of nicenames to exclude. Default empty array. */
|
|
15
|
+
nicenameNotIn?: string[];
|
|
16
|
+
/** The user display name. Default empty. */
|
|
17
|
+
displayName?: string;
|
|
18
|
+
/** An array of display names to include. Default empty array. */
|
|
19
|
+
displayNameIn?: string[];
|
|
20
|
+
/** An array of display names to exclude. Default empty array. */
|
|
21
|
+
displayNameNotIn?: string[];
|
|
22
|
+
/** The user login. Default empty. */
|
|
23
|
+
login?: string;
|
|
24
|
+
/** An array of logins to include. Default empty array. */
|
|
25
|
+
loginIn?: string[];
|
|
26
|
+
/** An array of logins to exclude. Default empty array. */
|
|
27
|
+
loginNotIn?: string[];
|
|
28
|
+
/** An array of role names. Matched users must have at least one of these roles. Default empty array. */
|
|
29
|
+
roleIn?: string[];
|
|
30
|
+
/** An array of role names that users must match to be included in results. Note that this is an inclusive list: users must match *each* role. Default empty. */
|
|
31
|
+
roleAnd?: string[];
|
|
32
|
+
/** An array of role names to exclude. Users matching one or more of these roles will not be included in results. Default empty array. */
|
|
33
|
+
roleNotIn?: string[];
|
|
34
|
+
/** Search keyword. Searches for possible string matches on columns. */
|
|
35
|
+
search?: string;
|
|
36
|
+
/** Array of column names to be searched. */
|
|
37
|
+
search_columns?: ('ID' | 'userLogin' | 'userEmail' | 'userUrl' | 'userNicename' | 'displayName')[]
|
|
38
|
+
/** Field(s) to order terms by. Default 'none'. */
|
|
39
|
+
orderBy?:
|
|
40
|
+
| 'ID'
|
|
41
|
+
| 'name'
|
|
42
|
+
| 'login'
|
|
43
|
+
| 'nicename'
|
|
44
|
+
| 'first_name'
|
|
45
|
+
| 'last_name'
|
|
46
|
+
| 'email'
|
|
47
|
+
| 'registered'
|
|
48
|
+
| 'post_count'
|
|
49
|
+
| 'count'
|
|
50
|
+
| 'none'
|
|
51
|
+
| string;
|
|
52
|
+
/** Designates ascending or descending order of users. Accepts 'ASC', 'DESC'. Default 'ASC'. */
|
|
53
|
+
order?: 'ASC' | 'DESC';
|
|
54
|
+
/** Number of users to offset in retrieved results. Can be used in conjunction with pagination. Default 0. */
|
|
55
|
+
offset?: number;
|
|
56
|
+
/** Number of users to limit the query for. Value -1 (all) is supported. Default -1. */
|
|
57
|
+
usersPerPage?: number;
|
|
58
|
+
/** Show users that would show up on page. */
|
|
59
|
+
page?: number;
|
|
60
|
+
/** Show all posts (true) or paginate (false). Default false. */
|
|
61
|
+
noPaging?: boolean;
|
|
62
|
+
/** Whether to skip counting the total rows found. Default false. */
|
|
63
|
+
noFoundRows?: boolean;
|
|
64
|
+
/** Pass an array of post types to filter results to users who have published posts in those post types. `true` is an alias for all public post types. */
|
|
65
|
+
hasPublishedPosts?: boolean | string[];
|
|
66
|
+
/** Meta query */
|
|
67
|
+
metaQuery?: {
|
|
68
|
+
/** Meta key */
|
|
69
|
+
metaKey: string,
|
|
70
|
+
/** Variable name to save result to */
|
|
71
|
+
as: string
|
|
72
|
+
}[]
|
|
73
|
+
/** Whether to get multiple, default false */
|
|
74
|
+
multiple?: bool
|
|
75
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { wpdb } from "@/wpdb/wpdb";
|
|
2
|
+
import { IUser } from "../../entities/user/user.interface";
|
|
3
|
+
import { EntityQuery } from "../../globals/entity-loader/entity-loader";
|
|
4
|
+
import { sql } from "kysely";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Executes database queries to retrieve user IDs and counts based on provided arguments.
|
|
8
|
+
*/
|
|
9
|
+
export class UserQuery implements EntityQuery<IUser>
|
|
10
|
+
{
|
|
11
|
+
private userCount?: number;
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
private args: UserQueryArgs
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
public getCount(): number | undefined {
|
|
18
|
+
return this.userCount;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async getIds(): Promise<number[]> {
|
|
22
|
+
let query = wpdb.selectFrom('wpUsers');
|
|
23
|
+
|
|
24
|
+
// --- ID Filters ---
|
|
25
|
+
if (this.args.userId) {
|
|
26
|
+
const types = Array.isArray(this.args.userId) ? this.args.userId : [this.args.userId];
|
|
27
|
+
query = query.where('wpUsers.ID', 'in', types);
|
|
28
|
+
}
|
|
29
|
+
if (this.args.userIdsNotIn) query = query.where('wpUsers.ID', 'not in', this.args.userIdsNotIn);
|
|
30
|
+
|
|
31
|
+
// --- Nicename Filters ---
|
|
32
|
+
if (this.args.nicename) query = query.where('wpUsers.userNicename', '=', this.args.nicename);
|
|
33
|
+
if (this.args.nicenameIn) query = query.where('wpUsers.userNicename', 'in', this.args.nicenameIn);
|
|
34
|
+
if (this.args.nicenameNotIn) query = query.where('wpUsers.userNicename', 'not in', this.args.nicenameNotIn);
|
|
35
|
+
|
|
36
|
+
// --- Display name Filters ---
|
|
37
|
+
if (this.args.displayName) query = query.where('wpUsers.displayName', '=', this.args.displayName);
|
|
38
|
+
if (this.args.displayNameIn) query = query.where('wpUsers.displayName', 'in', this.args.displayNameIn);
|
|
39
|
+
if (this.args.displayNameNotIn) query = query.where('wpUsers.displayName', 'not in', this.args.displayNameNotIn);
|
|
40
|
+
|
|
41
|
+
// --- Login Filters ---
|
|
42
|
+
if (this.args.login) query = query.where('wpUsers.userLogin', '=', this.args.login);
|
|
43
|
+
if (this.args.loginIn) query = query.where('wpUsers.userLogin', 'in', this.args.loginIn);
|
|
44
|
+
if (this.args.loginNotIn) query = query.where('wpUsers.userLogin', 'not in', this.args.loginNotIn);
|
|
45
|
+
|
|
46
|
+
// --- Role Filters ---
|
|
47
|
+
if (this.args.roleIn) {
|
|
48
|
+
query = query
|
|
49
|
+
.innerJoin('wpUsermeta as um_role_in', 'wpUsers.ID', 'um_role_in.userId')
|
|
50
|
+
.where('um_role_in.metaKey', '=', 'wp_capabilities')
|
|
51
|
+
.where((eb) => {
|
|
52
|
+
const orClauses = this.args.roleIn!.map(role =>
|
|
53
|
+
eb('um_role_in.metaValue', 'like', `%"${role}"%`)
|
|
54
|
+
);
|
|
55
|
+
return eb.or(orClauses);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (this.args.roleAnd) {
|
|
60
|
+
this.args.roleAnd.forEach((role, index) => {
|
|
61
|
+
const alias = `um_role_and_${index}` as const;
|
|
62
|
+
|
|
63
|
+
query = query
|
|
64
|
+
.innerJoin(`wpUsermeta as ${alias}`, 'wpUsers.ID', `${alias}.userId`)
|
|
65
|
+
.where(`${alias}.metaKey`, '=', 'wp_capabilities')
|
|
66
|
+
.where(`${alias}.metaValue`, 'like', `%"${role}"%`);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this.args.roleNotIn) {
|
|
71
|
+
this.args.roleNotIn.forEach((role, index) => {
|
|
72
|
+
const alias = `um_role_not_${index}` as const;
|
|
73
|
+
|
|
74
|
+
query = query
|
|
75
|
+
.innerJoin(`wpUsermeta as ${alias}`, 'wpUsers.ID', `${alias}.userId`)
|
|
76
|
+
.where(`${alias}.metaKey`, '=', 'wp_capabilities')
|
|
77
|
+
.where(`${alias}.metaValue`, 'not like', `%"${role}"%`);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// --- Search Filters ---
|
|
82
|
+
if (this.args.search) {
|
|
83
|
+
const searchTerm = `%${this.args.search}%`;
|
|
84
|
+
const searchCols = this.args.search_columns || ['ID', 'userLogin', 'userEmail', 'userUrl', 'userNicename', 'displayName'];
|
|
85
|
+
|
|
86
|
+
query = query.where((eb) => {
|
|
87
|
+
const orClauses = searchCols.map(col => eb(col, 'like', searchTerm));
|
|
88
|
+
return eb.or(orClauses);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// --- Published Posts Filter ---
|
|
93
|
+
if (this.args.hasPublishedPosts) {
|
|
94
|
+
query = query.where((eb) => eb(
|
|
95
|
+
eb.selectFrom('wpPosts')
|
|
96
|
+
.select(eb.fn.count<number>('ID').as('count'))
|
|
97
|
+
.whereRef('wpPosts.postAuthor', '=', 'wpUsers.ID')
|
|
98
|
+
.where('wpPosts.postStatus', '=', 'publish'),
|
|
99
|
+
'>', 0
|
|
100
|
+
));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 3. Calculate Total Count (Before Limits/Offsets)
|
|
104
|
+
if (!this.args.noFoundRows) {
|
|
105
|
+
try {
|
|
106
|
+
const countQueryBase = query.clearSelect().select('wpUsers.ID').distinct();
|
|
107
|
+
const countResult = await wpdb.selectFrom(countQueryBase.as('sub'))
|
|
108
|
+
.select(sql<number>`count(*)`.as('count'))
|
|
109
|
+
.executeTakeFirst();
|
|
110
|
+
|
|
111
|
+
this.userCount = countResult ? Number(countResult.count) : undefined;
|
|
112
|
+
} catch (error: any) {
|
|
113
|
+
console.error('WPUserQuery: Cannot get row count: ', error.message);
|
|
114
|
+
this.userCount = undefined;
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
this.userCount = undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 4. Apply ORDER BY
|
|
121
|
+
const orderDirection = this.args.order === 'DESC' ? 'desc' : 'asc';
|
|
122
|
+
const orderBy = this.args.orderBy || 'none';
|
|
123
|
+
|
|
124
|
+
switch(orderBy) {
|
|
125
|
+
case 'ID': query = query.orderBy('wpUsers.ID', orderDirection).orderBy('wpUsers.ID', orderDirection); break;
|
|
126
|
+
case 'name': query = query.orderBy('wpUsers.displayName', orderDirection).orderBy('wpUsers.ID', orderDirection); break;
|
|
127
|
+
case 'login': query = query.orderBy('wpUsers.userLogin', orderDirection).orderBy('wpUsers.ID', orderDirection); break;
|
|
128
|
+
case 'nicename': query = query.orderBy('wpUsers.userNicename', orderDirection).orderBy('wpUsers.ID', orderDirection); break;
|
|
129
|
+
case 'email': query = query.orderBy('wpUsers.userEmail', orderDirection).orderBy('wpUsers.ID', orderDirection); break;
|
|
130
|
+
case 'registered': query = query.orderBy('wpUsers.userRegistered', orderDirection).orderBy('wpUsers.ID', orderDirection); break;
|
|
131
|
+
case 'post_count':
|
|
132
|
+
query = query.orderBy(
|
|
133
|
+
(eb) => eb.selectFrom('wpPosts')
|
|
134
|
+
.select(eb.fn.count('ID').as('count'))
|
|
135
|
+
.whereRef('wpPosts.postAuthor', '=', 'wpUsers.ID')
|
|
136
|
+
.where('wpPosts.postStatus', '=', 'publish'),
|
|
137
|
+
orderDirection
|
|
138
|
+
).orderBy('wpUsers.ID', orderDirection);
|
|
139
|
+
break;
|
|
140
|
+
case 'none': break;
|
|
141
|
+
default:
|
|
142
|
+
query = query.orderBy('wpUsers.userLogin', orderDirection).orderBy('wpUsers.ID', orderDirection);
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 5. Apply LIMIT & OFFSET
|
|
147
|
+
if (!this.args.noPaging) {
|
|
148
|
+
const perPage = this.args.usersPerPage ?? -1;
|
|
149
|
+
|
|
150
|
+
if (perPage > -1) {
|
|
151
|
+
query = query.limit(perPage);
|
|
152
|
+
|
|
153
|
+
const page = this.args.page ?? 1;
|
|
154
|
+
const baseOffset = this.args.offset ?? 0;
|
|
155
|
+
const offsetAmount = ((page - 1) * perPage) + baseOffset;
|
|
156
|
+
|
|
157
|
+
if (offsetAmount > 0) {
|
|
158
|
+
query = query.offset(offsetAmount);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (this.args.metaQuery) {
|
|
164
|
+
let dynamicQuery: any = query;
|
|
165
|
+
|
|
166
|
+
for (let metaQuery of this.args.metaQuery) {
|
|
167
|
+
const uniqueAlias = `meta_${metaQuery.as}`;
|
|
168
|
+
|
|
169
|
+
dynamicQuery = dynamicQuery.leftJoin(`wpUsermeta as ${uniqueAlias}`, (join: any) =>
|
|
170
|
+
join
|
|
171
|
+
.onRef(`${uniqueAlias}.userId`, '=', 'wpUsers.ID')
|
|
172
|
+
.on(`${uniqueAlias}.metaKey`, '=', metaQuery.metaKey)
|
|
173
|
+
)
|
|
174
|
+
.select(`${uniqueAlias}.metaValue as ${metaQuery.as}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
query = dynamicQuery;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
if (this.args.multiple !== false) {
|
|
182
|
+
return (await query
|
|
183
|
+
.select('wpUsers.ID as id')
|
|
184
|
+
.groupBy('wpUsers.ID')
|
|
185
|
+
.execute()).map(res => res.id);
|
|
186
|
+
} else {
|
|
187
|
+
return (await query
|
|
188
|
+
.select('wpUsers.ID as id')
|
|
189
|
+
.execute()).map(res => res.id);
|
|
190
|
+
}
|
|
191
|
+
} catch (error: any) {
|
|
192
|
+
throw new Error(`WPUserQuery: Cannot get users: ${error.message}`, { cause: error });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves page number from path array.
|
|
3
|
+
*
|
|
4
|
+
* @param {string[]} path - Array of path segments.
|
|
5
|
+
* @returns {number | undefined} Page number or undefined.
|
|
6
|
+
*/
|
|
7
|
+
export function getPageNumber(path: string[]): number|undefined {
|
|
8
|
+
return path[path.length - 2] === 'page' ? Number(path[path.length - 1]) || undefined : undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Splits path string into array of segments.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} path - Path string.
|
|
15
|
+
* @returns {string[]} Array of path segments.
|
|
16
|
+
*/
|
|
17
|
+
export function splitPath(path: string): string[] {
|
|
18
|
+
if (!path) return [];
|
|
19
|
+
return path.split('/').filter(Boolean);
|
|
20
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fonts from "@/fonts";
|
|
2
|
+
import { LayoutTemplate } from "@/app/_templates/layout";
|
|
3
|
+
import { cookies, draftMode } from "next/headers";
|
|
4
|
+
import { getLanguageAttributes } from "@/services/metadata/get-language-attribute";
|
|
5
|
+
import { RenderTheAdminBar } from "@/ui/render-the-admin-bar";
|
|
6
|
+
|
|
7
|
+
const fontClasses = fonts.map(font => font.className).join(' ');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The layout for the application.
|
|
11
|
+
* Defines the main HTML shell, handles Draft Mode validation, primes necessary global WordPress options,
|
|
12
|
+
* and routes directly to the `LayoutTemplate` within the Nextpress template hierarchy.
|
|
13
|
+
*
|
|
14
|
+
* @param {Readonly<{children: React.ReactNode;}>} props - Component properties, wrapping the children pages.
|
|
15
|
+
* @returns {Promise<JSX.Element>} The root layout of the entire app.
|
|
16
|
+
*/
|
|
17
|
+
export async function NextpressLayout({ children }: Readonly<{children: React.ReactNode;}>) {
|
|
18
|
+
const draftModeEnabled = (await draftMode()).isEnabled;
|
|
19
|
+
let loggedInUserId = 0;
|
|
20
|
+
if (draftModeEnabled) {
|
|
21
|
+
const cookieStore = await cookies();
|
|
22
|
+
loggedInUserId = Number(cookieStore.get('nextpress_logged_in_user_id')?.value) || 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
optionLoader.findAndPrime({
|
|
26
|
+
column: 'optionName',
|
|
27
|
+
operand: 'in',
|
|
28
|
+
value: nextpressConfig.preLoadOptions || ''
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const languageAttributes = await getLanguageAttributes();
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<html
|
|
35
|
+
lang={languageAttributes}
|
|
36
|
+
className={fontClasses}
|
|
37
|
+
>
|
|
38
|
+
<body className="">
|
|
39
|
+
{(draftModeEnabled && (await getUser(loggedInUserId))?.showAdminBar) && <RenderTheAdminBar loggedInUserId={loggedInUserId}/>}
|
|
40
|
+
<LayoutTemplate>
|
|
41
|
+
{children}
|
|
42
|
+
</LayoutTemplate>
|
|
43
|
+
</body>
|
|
44
|
+
</html>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|