MediaWiki - How do I get the total number of Wanted pages? - mediawiki

In MediaWiki it's possible to access the total number of pages that have been created with one of the magic words, {{NUMBEROFPAGES}}. Is there a way to access the total number of wanted pages, pages that are redlinked? There is Special:WantedPages, and if I go to the last page of results it has the total number. Does it find that only on request, or is that variable accessible in a similar way to {{NUMBEROFPAGES}}?

That's not included in the default magic words and I'm not aware of any extension that would provide it, either. If you don't care too much about performance it is pretty easy to do:
global $wgHooks;
$wgHooks['LanguageGetMagic'][] = function ( &$magicWords, $langCode ) {
// 1 is for case-sensitive
$magicWords['wantedpages'] = [ 1, 'NUMBEROFWANTEDPAGES' ];
};
$wgHooks['MagicWordwgVariableIDs'][] = function ( &$customVariableIds ) {
$customVariableIds[] = 'wantedpages';
};
$wgHooks['ParserGetVariableValueSwitch'][] = function (
&$parser, &$cache, &$magicWordId, &$ret
) {
$db = wfGetDB( DB_REPLICA );
$ret = $db->selectRowCount(
[ 'pagelinks', 'page' ], // tables
'count(*)', //value
[ 'page_id' => null ], // conditions
__METHOD__,
[ 'GROUP BY' => [ 'pl_namespace', 'pl_title' ] ],
[ 'page' => [ 'LEFT JOIN', 'pl_namespace = page_namespace AND pl_title = page_title' ] ] // join conditions
);
};
Note that this is the extreme corner cutter version. If you are writing an extension and want to do it properly, see the manual.

Related

Combine two Json files exported from wordpress

I have two Json files that I exported from wordpress that have corresponding ID's I want to combine them into one Json file so I can bring it into website I am building with Gatsby JS. One of the files is the posts.json and the other is postsMeta.json. The post_id in postsMeta corresponds with the ID in Posts
How would I best go about merging the two? Can I run some sort of for loop in js and how would I so? I am on windows is there a json explorer of some sorts that could help me do this.
lastly I would also like to trim out some of the unnecasry fiels such as post_parent in the posts json and something like the meta_key in the postsMeta json.
Ok hopefully this is clear enough, thanks in advance.
Here is an example of the first object corresponding pairs in the two files
posts.json
{"ID":"19","post_author":"2","post_date":"2010-12-31 23:02:04","post_date_gmt":"2010-12-31 23:02:04","post_content":"Harry Potter was not available for the first sitting of the Halloween Picture. I hope everyone had a safe and fun Halloween. Tomorrow is picture retake day, please send back your previous prints if you want retakes. It is also hot lunch. See You tomorrow!","post_title":"Happy Halloween","post_excerpt":"","post_status":"publish","comment_status":"open","ping_status":"open","post_password":"","post_name":"happy-halloween","to_ping":"","pinged":"","post_modified":"2011-01-03 05:26:11","post_modified_gmt":"2011-01-03 05:26:11","post_content_filtered":"","post_parent":"0","guid":"http:\/\/localhost\/mrskitson.ca_wordpress\/?p=19","menu_order":"0","post_type":"post","post_mime_type":"","comment_count":"1"},
postsMeta.json
{"meta_id":"27","post_id":"19","meta_key":"large_preview","meta_value":"http:\/\/www.mrskitson.ca\/wp-content\/uploads\/2010\/12\/halloween.jpg"},
Update:
this is an attempt to solve this problem with the current answer, you can edit the code there.
How would I best go about merging the two?
Is it mandatory for you combine the two JSON files/data?
Because you could just require or load the JSON data from within your script (or even put them in the HTML) and then to get the meta value of a specific meta field/key, this function could do that:
// `single` has no effect if `meta_key` is empty.
function getPostMeta( post_id, meta_key, single ) {
let id = String( post_id ),
pm = [];
postsMeta.map( m => {
let a = ( ! meta_key ) ||
( meta_key === m.meta_key );
if ( a && id === m.post_id ) {
pm.push( m );
}
});
let meta = {},
mk = {};
pm.map( m => {
let k = m.meta_key, v;
if ( undefined === meta[ k ] ) {
meta[ k ] = m.meta_value;
} else {
v = meta[ k ];
if ( undefined === mk[ k ] ) {
meta[ k ] = [ v ];
mk[ k ] = 1;
}
meta[ k ].push( m.meta_value );
m[ k ]++;
}
});
pm = null;
mk = meta_key ? mk[ meta_key ] : null;
if ( mk ) {
return single ?
meta[ meta_key ][0] : // Returns a single meta value.
meta[ meta_key ]; // Returns all the meta values.
}
return meta_key ?
meta[ meta_key ] : // Returns the value of the `meta_key`.
meta; // Or returns all the post's meta data.
}
The data I used for testing: (take note of the postsMeta in the above/getPostMeta() function)
// Array of `post` objects.
const posts = [{"ID":"19","post_author":"2","post_date":"2010-12-31 23:02:04","post_date_gmt":"2010-12-31 23:02:04","post_content":"Harry Potter was not available for the first sitting of the Halloween Picture. I hope everyone had a safe and fun Halloween. Tomorrow is picture retake day, please send back your previous prints if you want retakes. It is also hot lunch. See You tomorrow!","post_title":"Happy Halloween","post_excerpt":"","post_status":"publish","comment_status":"open","ping_status":"open","post_password":"","post_name":"happy-halloween","to_ping":"","pinged":"","post_modified":"2011-01-03 05:26:11","post_modified_gmt":"2011-01-03 05:26:11","post_content_filtered":"","post_parent":"0","guid":"http:\/\/localhost\/mrskitson.ca_wordpress\/?p=19","menu_order":"0","post_type":"post","post_mime_type":"","comment_count":"1"}];
// Array of `meta` objects.
const postsMeta = [{"meta_id":"27","post_id":"19","meta_key":"large_preview","meta_value":"http:\/\/www.mrskitson.ca\/wp-content\/uploads\/2010\/12\/halloween.jpg"},{"meta_id":"28","post_id":"19","meta_key":"many_values","meta_value":"http:\/\/facebook.com"},{"meta_id":"29","post_id":"19","meta_key":"many_values","meta_value":"http:\/\/twitter.com"},{"meta_id":"30","post_id":"19","meta_key":"many_values","meta_value":"http:\/\/linkedin.com"}];
Examples: (see this Fiddle for demo)
// In these examples, we are retrieving the meta value for the post #19 (i.e. ID is 19).
// Retrieve a single value.
// Returns mixed; string, number, etc.
let url = getPostMeta( 19, 'large_preview', true );
console.log( url );
// Retrieve all meta values.
// Always returns an array of values.
let ms = getPostMeta( 19, 'many_values' );
console.log( ms, ms[0] );
// Retrieve all meta data.
// Always returns an object with meta_key => meta_value pairs. I.e. { key => value, ... }
let ma = getPostMeta( 19 );
console.log( ma, ma.large_preview, ma.many_values[0] );
But if you really must combine the JSON data, you can do: (again, see demo on the same Fiddle)
// Here we modify the original `posts` object.
posts.map( p => {
// Add all the post's meta data.
p.meta = getPostMeta( p.ID );
// Delete items you don't want..
delete p.post_parent;
delete p.menu_order;
// delete ...;
});
console.log( JSON.stringify( posts[0].meta ) ); // posts[0].meta = object
console.log( posts[0].post_parent, posts[0].menu_order ); // both are undefined
And then if you want to copy-paste the new/merged JSON data:
JSON.stringify( posts );
But if you actually just want to do something with the post's meta, you can loop through the posts object and do the thing; e.g.:
// Here the original `posts` object is not modified, and that we don't
// (though you can) repeatedly call `getPostMeta()` for the same post.
posts.map( p => {
// Get all the post's meta data.
let meta = getPostMeta( p.ID );
// Do something with `meta`.
console.log( meta.large_preview );
});
console.log( JSON.stringify( posts[0].meta ) ); // posts[0].meta = undefined
console.log( posts[0].post_parent, posts[0].menu_order ); // both still defined
// posts[0].meta wouldn't be undefined if of course posts[0] had a `meta` item,
// which was set in/via WordPress...
If you can do this in js, there's a pretty easy approach using Array#map. If you simplify your question, you're really asking how to add this meta data under each entry in posts, and get only the fields you want.
I'm assuming the posts.json is actually an array (e.g. [{"ID":"19"....).
// Load these server-side, fetch them remotely, copy-paste, etc.
// I'll require them here for simplicity
const posts = require('./posts.json');
const postsMeta = require('./postsMeta.json');
// Build a Map so we can quickly look up the metas by post_id
// Extract what we need by destructuring the args
const metaByPost = postsMeta.reduce((a, {
post_id: id,
meta_value: value,
}) => a.set(id, {
value,
/* anything else you want in here */,
}), new Map());
const mergedPosts = posts.map(post => ({
// Spread in the post
...post,
// Spread in the meta content
...metaByPost.get(post.ID),
// Undefine the props we don't want
post_parent: undefined,
}));
I don't love manually setting stuff to undefined -- I think it's nicer to explicitly say what props you're going to include, instead of loading everything and undefining certain props.
Try this snippet directly in the Chrome DevTools console:
(function(
postsUrl='https://cdn.glitch.com/61300ea6-6cc4-4cb6-a62f-31adc62ea5cc%2Fposts.json?1525386749382',
metaUrl='https://cdn.glitch.com/61300ea6-6cc4-4cb6-a62f-31adc62ea5cc%2Fpostmeta.json?1525386742630'
) {
Promise.all([
fetch(postsUrl).then(r => r.json()),
fetch(metaUrl).then(r => r.json()),
]).then(([postsResponse, metaResponse]) => {
// Inspected the actual JSON response to come up with the data structure
const posts = postsResponse[2].data;
const meta = metaResponse[2].data;
const metaByPostId = meta.reduce((accum, el) => {
accum[el.post_id] = el;
return accum;
}, {});
const transformedPosts = posts.map(post => {
const merged = {
...post,
...(metaByPostId[post.ID] || {}),
};
delete merged.post_parent;
// delete any other fields not wanted in the result
return merged;
});
console.log(transformedPosts);
});
})();
replace URLs accordingly, I used the ones from Glitch example here
as commented, actual data is buried in response[2].data. Use Network tab / Parsed view to see structure
replace console.log with copy, if you want the result copied to clipboard, instead of logged to console
Blunt to the point for your question. We want to:
merge var a = {/*some json*/} into var b = {/*another json*/}
trim fields in var exclusions = ["post_parent","meta_key"]
Merge the JSONS
First, we need to populate a and b.
Your JSONs are parsable into Javascript objects with JSON.parse():
let a = JSON.parse(/*JSON here*/);
let b = JSON.parse(/*JSON here*/);
Because how properties are defined in Javascript, if you define a property again, the second definition will overwrite the first. Your JSONS contain only strings as keys and strings as values, so a shallow copy will suffice. Object.assign() will copy all the properties (field and values) into the first argument and return the final Object. Therefore this will merge a into b, assuming they have different keys, else the values in b will overwrite values in a:
a = Object.assign(a,b);
Otherwise, if they are not disjoint, you have to define some policy on how to join, for example may prioritize one. Below, we keep the values in a instead:
a = Object.assign(b,a);
Since you mentionmed a for loop, the line below does the same as two code lines above and will also allow show you an example on how to write your own custom lambda expression:
Object.keys(a).forEach(k=>b[k]=b[k]?b[k]:a[k]);
Do not wish to touch a and b? Create a third object c.
let c = Object.assign({},a,b)
Lastly (wait until the trim step below is accomplished) JSON.stringify() will convert your merged object back into JSON.
Trim exclusions
Following the third example, we have c merged with all the fields.
First a little hack taken from here:
Object.filter = (obj, predicate) => Object.keys(obj)
.filter( key => predicate(obj[key]))
.reduce( (res, key) => (res[key] = obj[key], res), {} );
Now Objects, just like arrays have a filter prototype, having extended Object prototype. It is not really best practice since this will extend every Object but this function works quite well with respects to the semantics of Javascript and this example serves as an opportunity to keep elegant Javascript styles code:
c = Object.filter(c, key=> !exclusions.includes(key) );
Voit-lá, done.
As for defined Object.filter() it uses Array.filter() and Array.reduce() . Click for reference, for your convenience.

Kartik's star-rating widget: Plugin Methods

INTRODUCTION:
I have a blog maked on Yii2. The widget used for making rating is:
http://demos.krajee.com/widget-details/star-rating#basic-usage
http://plugins.krajee.com/star-rating#installation
Each post from blog have his AVERAGE RATING that is read from database.
After the user selects his own rating - segregate rating, the AVERAGE RATING is calculated in RatingController and recorded in database.
QUESTION:
How dynamically show the new calculated rating (AVERAGE RATING) after the user clicked on the star? Now for exemple (exemple with 2 voters) if the average rating is 4.5 and the user chooses for exemple 3.5, then after that it shows 3.5 instead 4. If updating page than it's ok, it shows 4. How to apply "Plugin Methods"?, for example 'update' or 'refresh'?
Well the concept is simple if you concentrate you have to update the ratings as soon as you click on the star and then you want to update the rating you need to use the plugin events and methods provided namely rating:change and update, use the rating:change to detect the click and make an ajax post request to the controller/action to update the rating in database ,and then send back the new rating as the response, then in the ajax success function update the star rating plugin with the new rating.
I will add the code that would emphasize the main logic rather than calculating the average.
Your star rating input code will look like the following look into the success function to see how to call the update method of starrating.
echo $formReview->field ( $review , 'rate' , [ 'inputOptions' =>
[ 'class' => 'form-control' ] ] )->widget ( StarRating::classname () , [
'pluginOptions' => [
'step' => 0.50 ,
'showClear' => true ,
'showCaption' => true ,
'filledStar' => '<i class="zmdi zmdi-star"></i>' ,
'emptyStar' => '<i class="zmdi zmdi-star-outline"></i>' ,
] ,
'pluginEvents' => [
'rating:change' => "function(event, value, caption){
$.ajax({
url:'path/to/update-rating',
method:'post',
data:{rate:value},
dataType:'json',
success:function(data){
$(event.currentTarget).rating('update',data.rating);
}
});
}"
]
] )->label ( false );
in your controller/action
public function actionUpdateRating() {
$response['success'] = false;
if ( Yii::$app->request->post () ) {
$id = Yii::$app->request->post ( 'blog_id' );
$model = Blog::findOne ( $id );
//update your database with the new rating
//............
//get new rating in $newRating
$response['rating'] = $newRating;
$response['success'] = true;
}
echo Json::encode ( $response );
Yii::$app->end ();
}

GetStream Laravel - Cannot batchadd to notification

I can't seem to use the batcher to copy an activity to notifications. I read from the docs
that there is a 100 limit to the TO field for copying activities, so i tried out using the batcher. but it does'nt seem to work. did I miss out something on the docs or on my code? if so, how do I get over the 100 limit?
$evtConvMention = array();
$evtConvMention[] = "event:{$event->id}";
$evtConvMention[] = "notification:1";
$evtConvMention[] = "notification:2";
$batcher = FeedManager::getClient()->batcher();
$batcher->addToMany([
"actor" => "App\User:{$user->id}",
"verb" => "eventpost",
"object" => "App\EventConversation:{$post->id}",
"foreign_id" => "App\EventConversation:{$post->id}"
], $evtConvMention);
The addToMany() call will have a similar limit. While I look into the PHP library a little more, it might be easier to use the To field in the activity payload itself.
$feed = FeedManager::getClient()->feed("event", $event->id);
$now = new \DateTime("now", new \DateTimeZone('Pacific/Nauru'));
$data = [
"actor" => "App\User:{$user->id}",
"verb" => "eventpost",
"object" => "App\EventConversation:{$post->id}",
"foreign_id" => "App\EventConversation:{$post->id}",
"time" => $now
];
$feed->addActivity($data);
We also HIGHLY recommend sending your own foreign_id and time fields in the payload as well (I've added an idea for the $now value in the code above, otherwise every feed will get its own unique record, which are a limited resource on your account.
If you have more than 100 notification feeds to write this into, it might be better to have the notification feeds for those users 'follow' the event feed. Then you don't need to use the to field at all.

Completely Remove Query Cache From CakePHP 2.x

I have the follow queries taking place
public function test()
{
$uuid = substr( String::uuid() , 4 , rand( 7 , 10 ) );
$name = $uuid;
$event = $this->Event->getEvent( array( "event_id" => "5240e695-9acc-4e32-9b98-1aecb3d0838" ) );
$event[ "event_name" ] = $name;
$this->Event->update( $event );
debug( $this->Event->search( array( "event_id" => $event[ "event_id"] ) )[ 0 ][ "event_name" ] );
debug( $this->Event->search( array( "event_id" => $event[ "event_id"] , "limit" => 1 ) )[ 0 ][ "event_name" ] );
}
I assign a random name to a particular event retrieved from a mySQL ( InnoDB ) table event, assign a random new name to it, and save it back in the database.
When I run the last two statements in this code, the results are not the same, note that the last parameter in the 2nd query simply adds LIMIT 1 to the end of the second query. ( I am not using CakePHP 2.x's typical methods for searching tables ). The result of the 1st search call will produce the previous result from the previous request while the 2nd search call will produce the actual currently updated name in the event table.
I've been searching high and low on how to remove query cacheing COMPLETELY from the datasource objects but can't figure out how. I've tried using flushMethodCache() on the datasource object, but it does nothing, I've tried setting
$cacheQueries = false, $cacheSources = false;
in the AppModel, but that doesn't do anything either. I've tried looking in /App/Config/Core.php and disabling cacheing, and setting the cache time to 0. None of this works.
Found out the problem:
Instead of calling
$model->query( $string );
I needed to call
$model->query( $string, false );

Wordpress - How to output all todays posts under all categories for json-api

I am customising the Wordpress plugin, JSON API. I want to output:
Display all published today's posts for each category in one big JSON tree.
IE:
Concept output:
Category 1
Post 1
Post 2
Category 2
No posts
Category 3
Post 1
Post 2
Post 3
I am following the tutorial about grabbing the latest posts by category however this seems to indicate I have to do 2 loops, one to grab all the categories and then another to grab each post within that category.
My custom function currently grabs the categories fine, but I am unsure of how to combine the two loops into a JSON tree using the current JSON API plugin.
// This is in the introspector.php file
public function get_todays_posts($today)
{
global $wpdb;
/* // Show posts for category 5 - this works, but it doesn't show the category name
$q = query_posts('cat=5&year='.$today["year"].'&monthnum='.$today["month"].'&day=' .$today["day"].'&showposts=-1&orderby=post_date&order=desc');
*/
// output for this is below
$sql = "SELECT * FROM gc_terms AS wterms INNER JOIN gc_term_taxonomy AS wtaxonomy ON ( wterms.term_id = wtaxonomy.term_id ) WHERE wtaxonomy.taxonomy = 'category'";
$data = $wpdb->get_results(($sql));
$results = array();
foreach ($data as $row)
{
$results[] = new JSON_API_Category($row);
}
return($results);
} // end function
My output is:
{
"status": "ok",
"count": 17,
"count_total": 4,
"pages": 1,
"posts": [
{
"id": 1,
"slug": "general",
"title": "General",
"description": "",
"parent": 0,
"post_count": 0
}
// etc
}
Ideally I would like to output all of todays posts under each category, but the problem that I am facing is how to do two loops and combine them into the JSON API plugin.
Any help or guidance on this would be most appreciated.
Thanks.
EDIT:
I have found a method which I have customized which gets me a step closer
global $json_api;
$category = $json_api->introspector->get_category_by_id(5);
if (!$category) {
$json_api->error("Not found.");
}
$posts = $json_api->introspector->get_posts(array(
'cat' => $category->id,
'post_status' => 'publish',
'year' => 2011,
'monthnum' => 10,
'day' => 01,
'orderby' => 'post_date',
'order' => 'ASC'
));
return $this->posts_object_result($posts, $category);
However, this is in-correct because posts are not within the category as seen in this screenshot from JSONPad.
I understand that I need to loop through the outer categories and then its children to get the output I want; however the problem is making json-api understand how to combine arrays into a valid JSON tree.
I have decided not to resolve this problem by getting the categories from the JSON API from my app and then getting the posts relevant to my category as-and-when I need them.
Its bit of a long-way round, but it will do for now.