For MessageContent::getTextForSummary(), the behaviour is exactly
the same;
For WikiPage::insertRedirectEntry(), the trailing ellipsis would
prevent landing on the position of the anchor;
For UserOptionsManager::saveOptionsInternal(), I also found the
trailing ellipsis is unnecessary, follow-up to commit 2a51865.
Bug: T326696
Bug: T207876
Change-Id: I2ec1878147cb0447794d2404db632733f83164cd
To avoid truncation by the database,
which can result in broken utf-8 letters and would break on strict mode.
This is just the backend part. The api should validate and provide a
better message to the user instead of hard truncation without feedback
Bug: T326696
Change-Id: Id80c81956f78f87f4a97bd03f467a194d826fb42
When calling UserOptionsManager to "change" an option to a value
equal to the default, when that option wasn't overriden (inherits the
default), it still sent a delete query to the database when we knew
the row doesn't exist.
Bug: T301506
Change-Id: I9bd1f188a977d966b40c1320c105dbc9bfd0eb3c
isset returns false for null and the null values are not compared,
that treats them as non-default, even the default is also null.
Bug: T291748
Follow-Up: I6e61c11d8aed27b4b559e74849e0056e5eef3638
Change-Id: I0a0932b403098967c261eee3dc0e7d5da3c4fffb
After the hooks were removed we are finally ready to stop
reading user options from primary before writing them on save.
The new save hooks only work on modified options, so options
saving code can be significantly simplified.
Change-Id: I48df616c9f35d9a0b2801ada1b7dbef0bd4ad058
This reverts commit 98878c08ba.
reason for revert: had some weird and unwanted side effects
Bug: T294265
Change-Id: I53c2175498af5b37096505dae011e65cebf029aa
Use the isValueEqual function from
I572446faa8d40801a9adc3aee4b26d97c18000a1 to remove more bool values,
if the default options and the user option differs in real type.
Bug: T291748
Change-Id: I6e61c11d8aed27b4b559e74849e0056e5eef3638
Prior to this change UserOptionsManager::saveOptions was
@internal, since it was not updating user_touched or calling
the UserSaveSettings hook, which some extensions might
have been expecting. This method however was already used
to save the user options.
This patch makes the UserOptionsManager::saveOptions
feature complete and removes the @internal tag.
Bug: T284354
Change-Id: Iadb723b78da04d02dad9abfbfcf13fa25a43a115
See full rationale at I59068cfed10aabf6c6002f9e9312a6ef6e7e9441.
Using IDatabase for now instead of DBConnRef for better BC.
Change-Id: Ie75aaf46ba91779e8706b10efeefa9580857f489
This allows hook callers to compare before and after the save
Bug: T287397
Depends-On: I20098ae076b282296670d1116e14bbd29ea76b11
Change-Id: I4d09008bc2bc10afc3742b74564e5ef90ecfe5bf
== Status quo ==
When saving user preferences, we want to lock the rows to avoid
accidentally overriding a concurrent options update. So usually
what extensions do is:
$value = $mngr->getOption( 'option', ..., READ_LOCKING );
if ( $value !== 'new_value' ) {
$mngr->setOption( 'option', 'new_value' );
$mngr->saveOptions()
}
Previously for extra caution we've ignored all caches in options
manager if >= READ_LOCKING flags were passed. This resulted in
re-reading all the options multiple times. At worst, 3 times:
1. If READ_NORMAL read was made for update - that's once,
2. On setOption, one more read, forcefully from primary
3. On saveOptions, one more read from primary, non-locking,
to figure out which option keys need to be deleted.
Also, READ_LOCKING was not used where it clearly had to be used,
for example right before the update. This was trying to fix any
kind of error on part of the manager clients, unsuccessfully so.
== New approach ==
1. Cache modified user options separately from originals and merge
them on demand. This means when refetching originals with LOCKING
we don't wipe out all modifications made to the cache with setOption.
Extra bonus - we no longer need to load all options to set an option.
2. Split the originals cache into 2 layers - one for stuff that
comes from DB directly, and one with applied normalizations and
whatever hooks modify. This let's us avoid refetching DB options
after we save them, but still let's the hooks execute on newly set
options after they're saved.
3. Cache options with all query flags. This is a bit controversial, but
ideally LOCKING flags will be applied on options fetch right before
they are saved. We have to re-read options with LOCKING in saveOptions
to avoid races, but if the caller did 'getOption( ..., LOCKING),
setOption(), save()' we will not need to re-select with LOCKING again.
Bug: T280220
Change-Id: Ibed2789f5260b725fd806b4470631aa30d814ce6
The parsing of the timecorrection useroption was split over multiple
classes. Combine into a single class and add some testcases.
Change-Id: I2cadac00e46dff2bc7d81ac2f294ea2ae4e72f47
This is going to fix the bug, but it's not going far enough, READ_LATEST
needs to be bumped to READ_EXCLUSIVE.
The problem is that the options mananger cache serves dual purpose:
on option lookup it's a cache, while it also holds the modifications
made to options before saving. We need to require the options fetched
with READ_EXCLUSIVE before we are going to save them, and not discard
the options cache if they were already read with READ_EXCLUSIVE. Relying
on the callers to use correct query flags seems very prone to errors.
Before this was handled with User::getInstanceForUpdate. I wonder if we
should establish a similar pattern and remove the query flags from the
individual methods paramaters, but establish that UserOptionsLookup
service, responsible for reading-only, will use replica DB, while
UserOptionsManager will use master and do locking reads. The usage
pattern would then be - if you only need to read the options, use
lookup. If you have an intention of modifying the options - grab
and instance of the manager, and go into master by default. Thoughts?
Bug: T255842
Change-Id: I399ab0da8880320fd9d5f725ead8a62026cd7b7d
- Don't care about $queryFlags for anons since nothing can be
stored in the database for anons.
- For loading the options - discard the old cache in case higher
query flags are used. This means that 'setOption' has to by default
reload the options to ensure changing the options start from LATEST.
This codepath shouldn't be executed in reality cause we should
be already loading the user with READ_LATEST if we want to update
the options. Where that was not done - it was probably a bug.
Also, expose optional $queryFlags parameter for UserOptionsLookup
methods. Otherwise there's no way to read from master using public API.
Bug: T248527
Change-Id: Id7b9868ecdfba89bfafd4618365fe520ec59fcfe
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
Since the hook interfaces are not yet released and adding a parameter
to the hook is b/c, I have just added a parameter without introducing
a new version of the hook interface
Bug: T253149
Change-Id: Iac6c4b706ddbc7b0c9fb0b40eba05bd3530b1fdf
This converts user options management to a separate
service for use in DI context.
User options are accessed quite early on in installation
process and full-on options management depends on the
database. Prior we have protected from accessing the DB
by setting a hacky $wgUser with 0 id, and relying on the
implementation that it doesn't go into the database to
get the default user options. Now we can't really do that
since DBLoadBalancer is required to instantiate the options
manager. Instead, we redefine the options manager with
a DefaultOptionsManager, that only provides access to
default options and doesn't require DB access.
UserOptionsManager uses PreferencesFactory, however
injecting it will produce a cyclic dependency. The problem
is that we separate options to different kinds, which are
inferred from the PreferencesFactory declaration for those
options (e.g. if it's a radio button in the UI declaration,
the option is of multiselect kind). This is plain wrong,
the dependency should be wise versa. This will be addressed
separately, since it's requires larger refactoring. For now
the PreferencesFactory is obtained on demand. This will be
addressed in a followup.
Bug: T248527
Change-Id: I74917c5eaec184d188911a319895b941ed55ee87