I'm building a Mediawiki site which will include a few thousand Bot-generated articles. I want users to be able to edit lower sections of each article, but not edit the bot-generated sections.
I found an abandoned extension called ProtectSection which did this, but I don't have the skills to update it to work with the current Mediawiki release.
I'm considering making the Bot-generated articles protected, and then transcluding them into user-editable articles. If I do that, can I hide the original Bot-generated articles from search engines, and from being navigable within the wiki?
Also, I'd like users to be able to reference prior versions of the bot-generated articles, as their text will be updated from time to time by the bot. If I transclude and hide the bot-generated articles, I'm assuming their history then will be inaccessible. This wouldn't be a problem if I could keep the bot-generated articles available, with user-editable sections in them.
I got a bad news. It's really difficult to make part of article protected. Current mediawiki architecture doesn't allow it from scratch.
What I suggest you to do is create custom namespase and place all bot's articles there.
// Define constants for my additional namespaces.
define("NS_FOO", 3000); // This MUST be even.
define("NS_FOO_TALK", 3001); // This MUST be the following odd integer.
// Add namespaces.
$wgExtraNamespaces[NS_FOO] = "Foo";
$wgExtraNamespaces[NS_FOO_TALK] = "Foo_talk"; // Note underscores in the namespace name.
Resrict ordinary users edit this custom namespace, here is some info. But allow users to watch history of this pages.
# Only allow autoconfirmed users to edit Project namespace
$wgNamespaceProtection[NS_PROJECT] = array( 'autoconfirmed' );
# Don't allow anyone to edit non-talk pages until they've confirmed their
# e-mail address (assuming we have no custom namespaces and allow edits
# from non-emailconfirmed users to start with)
# Note for 1.13: emailconfirmed group and right were removed from default
# setup, if you want to use it, you'll have to re-enable it manually
$wgNamespaceProtection[NS_MAIN] = $wgNamespaceProtection[NS_USER] =
$wgNamespaceProtection[NS_PROJECT] = $wgNamespaceProtection[NS_IMAGE] =
$wgNamespaceProtection[NS_TEMPLATE] = $wgNamespaceProtection[NS_HELP] =
$wgNamespaceProtection[NS_CATEGORY] = array( 'emailconfirmed' );
# Only allow sysops to edit "Policy" namespace
$wgGroupPermissions['sysop']['editpolicy'] = true;
$wgNamespaceProtection[NS_POLICY] = array( 'editpolicy' );
Last step you already know - use Tranclution.
Related
I am learning MediaWiki 1.31.1 and hope to change default interface strings. For example, out-of-the-box signup page displays this:
Email address (optional)
I would like to show (remove optional)
Email address
I am able to hack the MediaWiki string file (/languages/i18n/en.json) and make changes there. Any better way?
If you are running a multilingual wiki, or if your users are likely to use different interface languages (such as when using the Universal Language Selector extension to automatically use visitor's preferred language), then you should use the MessageCache::get hook so that the customization is used in all languages. Otherwise user's using a different interface language will not see the customization.
It's used like this:
LocalSettings.php:
$wgHooks['MessageCache::get'][] = function ( &$key ) {
$keys = [ // The list of messages you want to customize
'prefs-help-realname',
'createacct-realname',
];
if ( in_array( $key, $keys, true ) ) {
$key = "myprefix-$key";
}
};
Then you need to edit https://example.org/wiki/MediaWiki:myprefix-key (replace with your wiki domain and key with the original message key (which you can find with uselang=qqx).
Append uselang=qqx to the URL to see message names instead of the text of the messages: https://en.wikipedia.org/wiki/Special:CreateAccount?uselang=qqx
You should only see the (optional) part though if you configured email to be optional, so I'm not sure the message text is your real problem.
Any language string can also be edited by going to the page MediaWiki:name-of-language-string on your wiki and editing that.
Just getting started with using chef recently. I gather that attributes are stored in one large monolithic hash named node that's available for use in your recipes and templates.
There seem to be multiple ways of defining attributes
Directly in the recipe itself
Under an attributes file - e.g. attributes/default.rb
In a JSON object that's passed to the chef-solo call. e.g. chef-solo -j web.json
Given the above 3, I'm curious
Are those all the ways attributes can be defined?
What's the order of precedence here? I'm assuming one of these methods supercedes the others
Is #3 (the JSON method) only valid for chef-solo ?
I see both node and default hashes defined. What's the difference? My best guess is that the default hash defined in attributes/default.rb gets merged into the node hash?
Thanks!
Your last question is probably the easiest to answer. In an attributes file you don't have to type 'node' so that this in attributes/default.rb:
default['foo']['bar']['baz'] = 'qux'
Is exactly the same as this in recipes/whatever.rb:
node.default['foo']['bar']['baz'] = 'qux'
In retrospect having different syntaxes for recipes and attributes is confusing, but this design choice dates back to extremely old versions of Chef.
The -j option is available to chef-client or chef-solo and will both set attributes. Note that these will be 'normal' attributes which are persistent in the node object and are generally not recommended to use. However, the 'run_list', 'chef_environment' and 'tags' on servers are implemented this way. It is generally not recommended to use other 'normal' attributes and to avoid node.normal['foo'] = 'bar' or node.set['foo'] = 'bar' in recipe (or attribute) files. The difference is that if you delete the node.normal line from the recipe the old setting on a node will persist, while if you delete a node.default setting out of a recipe then when your run chef-client on the node that setting will get deleted.
What happens in a chef-client run to make this happen is that at the start of the run the client issues a GET to get its old node document from the server. It then wipes the default, override and automatic(ohai) attributes while keeping the 'normal' attributes. The behavior of the default, override and automatic attributes makes the most sense -- you start over at the start of the run and then construct all the state, if its not in the recipe then you don't see a value there. However, normally the run_list is set on the node and nodes do not (often) manage their own run_list. In order to make the run_list persist it is a normal attribute.
The choice of the word 'normal' is unfortunate, as is the choice of 'node.set' setting 'normal' attributes. While those look like obvious choices to use to set attributes users should avoid using those. Again the problem is that they came first and were and are necessary and required for the run_list. Generally stick with default and override attributes only. And typically you can get most of your work done with default attributes, those should be preferred.
There's a big precedence level picture here:
https://docs.chef.io/attributes.html#attribute-precedence
That's the ultimate source of truth for attribute precedence.
That graph describes all the different ways that attributes can be defined.
The problem with Chef Attributes is that they've grown organically and sprouted many options to try to help out users who painted themselves into a corner. In general you should never need to touch automatic, normal, force_default or force_override levels of attributes. You should also avoid setting attributes in recipe code. You should move setting attributes in recipes to attribute files. What this leaves is these places to set attributes:
in the initial -j argument (sets normal attributes, you should limit using this to setting the run_state, over using this is generally smell)
in the role file as default or override precedence levels (careful with this one though because roles are not versioned and if you touch these attributes a lot you will cause production issues)
in the cookbook attributes file as default or override precedence levels (this is where you should set most of your attributes)
in environment files as default or override precedence levels (can be useful for settings like DNS servers in a datacenter, although you can use roles and/or cookbooks for this as well)
You also can set attributes in recipes, but when you do that you invariably wind up getting your next lesson in the two-phase compile-converge parser that runs through the Chef Recipes. If you have recipes that need to communicate with each other its better to use the node.run_state which is just a hash that doesn't get written as node attributes. You can drop node.run_state[:foo] = 'bar' in one recipe and read it in another. You probably will see recipes that set attributes though so you should be aware of that.
Hope That Helps.
When writing a cookbook, I visualize three levels of attributes:
Default values to converge successfully -- attributes/default.rb
Local testing override values -- JSON or .kitchen.yml (have you tried chef_zero using ChefDK and Kitchen?)
Environment/role override values -- link listed in lamont's answer: https://docs.chef.io/attributes.html#attribute-precedence
In Wintersmith, the default blog template generates posts from content/articles/<article>/index.md. This is fine as it allows associated files like images to be included with the article. But in practice, most "blog posts" are just text content associated with a template. Having to create subdirs is a minor annoyance, and if editing multiple entries in a tabbed editor, it's annoying having everything named index.md.
The site generator will spit out articles/basic-post.html files, but does not include these in the generated index or archive pages. How can I get the latter to work without breaking anything?
This may or may not be a simple problem, but I'm new to Wintersmith and haven't seen how to do this. I'm not sure it's as trivial as editing the default paginator (and I am not that used to CoffeeScript, which maybe it's time to address that :)
In paginator.coffee:
getArticles = (contents) ->
# helper that returns a list of articles found in *contents*
# note that each article is assumed to have its own directory in the articles directory
articles = contents[options.articles]._.directories.map (item) -> item.index
articles.sort (a, b) -> b.date - a.date
return articles
This looks like the place, however it seems like a bad idea to directly edit a plugin, for potential future updates to work.
Wintersmith is pretty awesome btw.
You were right: theanswer lies into the paginator plugin.
Wintersmith will constently watch the contents folder, building a ContentTree array.
That objet array will contain a descriptor for each file and folder within contents.
The getArticles just filter this possible candidates, and you just need to enhance it to get plain markdown files in the contents/articles folder.
getArticles = (contents) ->
# helper that returns a list of articles found in *contents*
# include articles with dedicated directory
articles = contents[options.articles]._.directories.map (item) -> item.index
# add articles that are in the *contents/articles* folder
articles = articles.concat contents[options.articles]._.pages
articles.sort (a, b) -> b.date - a.date
return articles
In WordPress there is Biographical Info under Profile. I would like to prevent the user from exceeding a maximum length of 400 characters. Also, the number of hyperlinks he can place in the biographical info should not exceed three. How do I do that? I am very familiar with JQuery, if that helps in this question. I am just a newbie with WordPress.
For the Javascript side, you should attach the necessary events to the description field. You can load your script via the wp_enqueue_script hook, and you probably want to do all this in your handler for admin_enqueue_scripts, where you check for the passed $hook_name, which in this case is the page name. It is user-edit.php when an admin edits a user, and profile.php when the user edits their own information (in which case IS_PROFILE_PAGE will also be defined as TRUE).
add_action('admin_enqueue_scripts', 'add_description_validation_script');
function add_description_validation_script($pagename) {
if ($pagename == 'profile.php' || $pagename == 'user-edit.php') {
wp_enqueue_script('description_validation', '/path/to/description_validation.js');
}
}
For the PHP side, you need the pre_user_description filter. This gives you the current biographical text, and your function can change this and return something else.
add_filter('pre_user_description', 'sanitize_description');
function sanitize_description($description)
{
// Do stuff with the description
return $description;
}
If, instead of silently changing the description, you want to show an error, you should look into the user_profile_update_errors hook. There you can validate the given data and return error messages to the user.
You probably want to wrap this all up in a plugin, so you can keep the code together and easily enable or disable it. A plugin is just a file in the /wp-content/plugins/ directory, most likely in a subdirectory named after your plugin.
I like Stack Overflow's URLs - specifically the forms:
/questions/{Id}/{Title}
/users/{Id}/{Name}
It's great because as the title of the question changes, the search engines will key in to the new URL but all the old URLs will still work.
Jeff mentioned in one of the podcasts - at the time the Flair feature was being announced - that he regretted some design decisions he had made when it came to these forms. Specifically, he was troubled by his pseudo-verbs, as in:
/users/edit/{Id}
/posts/{Id}/edit
It was a bit unclear which of these verb forms he ended up preferring.
Which pattern do you prefer (1 or 2) and why?
I prefer pattern 2 for the simple reason is that the URL reads better. Compare:
"I want to access the USERS EDIT resource, for this ID" versus
"I want to access the POSTS resource, with this ID and EDIT it"
If you forget the last part of each URL, then in the second URL you have a nice recovery plan.
Hi, get /users/edit... what? what do you want to edit? Error!
Hi, get /posts/id... oh you want the post with this ID hmm? Cool.
My 2 pennies!
My guess would be he preferred #2.
If you put the string first it means it always has to be there. Otherwise you get ugly looking urls like:
/users//4534905
No matter what you need the id of the user so this
/user/4534905/
Ends up looking better. If you want fakie verbs you can add them to the end.
/user/4534905/edit
Neither. Putting a non-English numeric ID in the URL is hardly search engine friendly. You are best to utliize titles with spaces replaced with dashes and all lowercase. So for me the correct form is:
/question/how-do-i-bake-an-apple-pie
/user/frank-krueger
I prefer the 2nd option as well.
But I still believe that the resulting URLs are ugly because there's no meaning whatsoever in there.
That's why I tend to split the url creation into two parts:
/posts/42
/posts/42-goodbye-and-thanks-for-all-the-fish
Both URLs refer to the same document and given the latter only the id is used in the internal query. Thus I can offer somewhat meaningful URLs and still refrain from bloating my Queries.
I like number 2:
also:
/questions/foo == All questions called "foo"
/questions/{id}/foo == A question called "foo"
/users/aiden == All users called aiden
/users/{id}/aiden == A user called aiden
/users/aiden?a=edit or /users/aiden/edit == Edit the list of users called Aiden?
/users/{id}/edit or /users/{id}?a=edit is better
/rss/users/aiden == An RSS update of users called aiden
/rss/users/{id} == An RSS feed of a user's activity
/rss/users/{id}/aiden == An RSS feed of Aiden's profile changes
I don't mind GET arguments personally and think that /x/y/z should refer to a mutable resource and GET/POST/PUT should act upon it.
My 2p
/question/how-do-i-bake-an-apple-pie
/question/how-do-i-bake-an-apple-pie-2
/question/how-do-i-bake-an-apple-pie-...