updated core to 7.58 (right after the site was hacked)
This commit is contained in:
339
sites/all/modules/contrib/localisation/tmgmt/LICENSE.txt
Normal file
339
sites/all/modules/contrib/localisation/tmgmt/LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
180
sites/all/modules/contrib/localisation/tmgmt/README.txt
Normal file
180
sites/all/modules/contrib/localisation/tmgmt/README.txt
Normal file
@@ -0,0 +1,180 @@
|
||||
Translation Management Tool (tmgmt)
|
||||
-------------------------------------
|
||||
|
||||
A collection of tools to facilitate the translation of text elements in Drupal.
|
||||
|
||||
Requirements
|
||||
------------------
|
||||
|
||||
Translation Management Tool was built for Drupal 7. There will be no backport.
|
||||
|
||||
To use Translation Management Tool you need to install and activate the
|
||||
following modules:
|
||||
|
||||
* Entity API
|
||||
* Views
|
||||
* Chaos Tools (Required for Views)
|
||||
* Views Bulk Operations
|
||||
* Content Translation
|
||||
* Locale
|
||||
* Rules
|
||||
|
||||
Optional dependencies:
|
||||
* Internationalization/i18n
|
||||
(Only necessary for i18n_string translation)
|
||||
* Entity Translation (only for entity sources)
|
||||
|
||||
Basic concepts
|
||||
------------------
|
||||
|
||||
With TMGMT installed, the 'translate' tab of a node changes. You can choose
|
||||
one or more languages to translate the node to and 'Request a translation' with
|
||||
the corresponding button.
|
||||
|
||||
A translation job is created for each language chosen. It will run through the
|
||||
following states:
|
||||
|
||||
Unprocessed Translation requested in the 'translate' tab of a node.
|
||||
Settings of the job (label set, translator chosen) defined.
|
||||
The job was saved.
|
||||
Active The job is in the process of being translated. Depending on
|
||||
the chosen translator, the actual translation happens auto-
|
||||
matically or by a human being.
|
||||
In all cases the job is returned to the job queue for review.
|
||||
When the review is done, the status of the job item goes from
|
||||
'needs review' to 'accepted'.
|
||||
Finished The job has been accepted and the translated node was created
|
||||
|
||||
The project also provides overviews for the supported sources that allow to
|
||||
translate multiple pieces of content (job items) in a single job and see the
|
||||
current translation status for your site content.
|
||||
|
||||
Getting started
|
||||
------------------
|
||||
|
||||
The first simple translation job using Microsoft's translation service.
|
||||
|
||||
1) Preparation
|
||||
|
||||
- Make sure you have downloaded all of the listed dependencies.
|
||||
- Define a second language using locale
|
||||
- Modify one content type to be multilingual. Choose 'Enabled, with translation'
|
||||
from the Publishing Options / Multilingual support.
|
||||
|
||||
2) Set up Translation Management Tool
|
||||
|
||||
- Download tmgmt module
|
||||
- Download tmgmt_microsoft module
|
||||
- Enable the following modules, this will also include all dependencies
|
||||
- Translation Management UI
|
||||
- Content translation Source UI
|
||||
- Microsoft Translator
|
||||
- A translator has been automatically created. Go to the Translator management
|
||||
page at:
|
||||
|
||||
Configuration > Regional and language > Translation Management Translators
|
||||
|
||||
Adjust the label to your liking and get a client ID and client secret using
|
||||
the provided link in the settings. Then save the updated translator.
|
||||
|
||||
- Adjust the Auto Acceptance settings to your liking. You can choose to accept
|
||||
jobs without review by checking 'Auto accept finished translations' for each
|
||||
of your translators individually.
|
||||
|
||||
3) Translate
|
||||
|
||||
- Create a new piece of content of the multilingual content type defined before.
|
||||
Make sure to choose a language.
|
||||
- Once the node has been saved, click on the "Translate" tab.
|
||||
- Choose the language you want to translate the node to with the checkbox.
|
||||
- Click on 'Request Translation' and the foreign language version of the node
|
||||
will be created immediately.
|
||||
- If the auto acceptance is not set, find the job in the jobs queue and choose
|
||||
the 'review' link. Accept the translation and the translated node is created.
|
||||
- Check the translated node!
|
||||
|
||||
For further options, see the documentation on http://drupal.org/node/1445790.
|
||||
|
||||
Features
|
||||
----------
|
||||
|
||||
This projects consists of 3 major parts. The starting point are the sources,
|
||||
which expose translatable content like nodes, other entities and i18n strings.
|
||||
|
||||
On the other side are the so called translators, which are responsible for
|
||||
getting the requested sources translated.
|
||||
|
||||
The core system combine these two parts and provide the ability to create,
|
||||
manage and review translation jobs.
|
||||
|
||||
The main features of the core system include:
|
||||
|
||||
- Creation of translations and managing their progress
|
||||
- Review of returned translations, ability to request revisions and communicate
|
||||
with the translator if supported.
|
||||
- Translation overviews that allow to see which content is available in which
|
||||
language and what translation jobs are currently ongoing.
|
||||
- The same information is provided on the translate tab of the supported
|
||||
sources.
|
||||
- A suggestions system that makes recommendations about related content that
|
||||
could be translated with the same job.
|
||||
- Sources can declare which parts of a source text should not be translated,
|
||||
for example placeholders for user interface strings.
|
||||
|
||||
The following sources are currently supported:
|
||||
|
||||
- Content Translation
|
||||
Integrates with the core translation module to translate nodes.
|
||||
|
||||
- Entity Translation
|
||||
Integrates with the entity_translation module that allows to translate fields
|
||||
on any entity type.
|
||||
|
||||
- Internationalization (i18n)
|
||||
Integrates with the i18n project (http://drupal.org/project/i18n) and allows
|
||||
to translate various configuration elements of a site: blocks, terms, fields,
|
||||
node types, contact categories and many more.
|
||||
|
||||
- Locale
|
||||
Allows to translate locale strings. Currently limited to the default
|
||||
textgroup (user interface strings passed through t()).
|
||||
|
||||
Two translators are included in the project:
|
||||
|
||||
- File translator
|
||||
Allows to export jobs into files and import them once they have been
|
||||
translated. Contains a pluggable system to support various file formats,
|
||||
currently XLIFF and HTML.
|
||||
|
||||
- Local Translator
|
||||
The local translator allows to manage translators on your own site so that
|
||||
they can translate your content in a central place and defined workflows.
|
||||
Together with the TMGMT Server, it can be used to build your own translation
|
||||
server. Check the Hermes installation profile for more information:
|
||||
http://drupal.org/project/hermes
|
||||
|
||||
Translators in separate projects:
|
||||
|
||||
- Microsoft Translator
|
||||
Machine translation using Microsoft's Bing translation.
|
||||
http://drupal.org/project/tmgmt_microsoft.
|
||||
|
||||
- Google
|
||||
Machine translation using Google Translate.
|
||||
Moved to http://drupal.org/project/tmgmt_google.
|
||||
|
||||
- Gengo (Previously named MyGengo)
|
||||
Human translation that integrates with http://www.gengo.com.
|
||||
http://drupal.org/project/tmgmt_mygengo.
|
||||
|
||||
- Supertext
|
||||
Human translation that integrates with http://www.supertext.ch.
|
||||
http://drupal.org/project/tmgmt_supertext.
|
||||
|
||||
- Nativy
|
||||
Human translation that integrates with http://www.nativy.com.
|
||||
http://drupal.org/project/tmgmt_nativy.
|
||||
|
||||
- One Hour Translation
|
||||
Human translation that integrates with http://www.onehourtranslation.com.
|
||||
http://drupal.org/project/tmgmt_oht
|
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the job entity controller class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller class for the job entity.
|
||||
*
|
||||
* @ingroup tmgmt_job
|
||||
*/
|
||||
class TMGMTJobController extends EntityAPIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($entity, DatabaseTransaction $transaction = NULL) {
|
||||
$entity->changed = REQUEST_TIME;
|
||||
return parent::save($entity, $transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($ids, $transaction = NULL) {
|
||||
parent::delete($ids, $transaction);
|
||||
// Since we are deleting one or multiple jobs here we also need to delete
|
||||
// the attached job items and messages.
|
||||
$query = new EntityFieldQuery();
|
||||
$result = $query->entityCondition('entity_type', 'tmgmt_job_item')
|
||||
->propertyCondition('tjid', $ids)
|
||||
->execute();
|
||||
if (!empty($result['tmgmt_job_item'])) {
|
||||
$controller = entity_get_controller('tmgmt_job_item');
|
||||
// We need to directly query the entity controller so we can pass on
|
||||
// the transaction object.
|
||||
$controller->delete(array_keys($result['tmgmt_job_item']), $transaction);
|
||||
}
|
||||
$query = new EntityFieldQuery();
|
||||
$result = $query->entityCondition('entity_type', 'tmgmt_message')
|
||||
->propertyCondition('tjid', $ids)
|
||||
->execute();
|
||||
if (!empty($result['tmgmt_message'])) {
|
||||
$controller = entity_get_controller('tmgmt_message');
|
||||
// We need to directly query the entity controller so we can pass on
|
||||
// the transaction object.
|
||||
$controller->delete(array_keys($result['tmgmt_message']), $transaction);
|
||||
}
|
||||
$query = new EntityFieldQuery();
|
||||
$result = $query->entityCondition('entity_type', 'tmgmt_remote')
|
||||
->propertyCondition('tjid', $ids)
|
||||
->execute();
|
||||
if (!empty($result['tmgmt_remote'])) {
|
||||
$controller = entity_get_controller('tmgmt_remote');
|
||||
$controller->delete(array_keys($result['tmgmt_remote']), $transaction);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the job item entity controller class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller class for the job item entity.
|
||||
*
|
||||
* @ingroup tmgmt_job
|
||||
*/
|
||||
class TMGMTJobItemController extends EntityAPIController {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo Eliminate the need to flatten and unflatten the JobItem data.
|
||||
*/
|
||||
public function save($entity, DatabaseTransaction $transaction = NULL) {
|
||||
$entity->changed = REQUEST_TIME;
|
||||
if (!empty($entity->tjid)) {
|
||||
$entity->recalculateStatistics();
|
||||
}
|
||||
return parent::save($entity, $transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($ids, $transaction = NULL) {
|
||||
parent::delete($ids, $transaction);
|
||||
// Since we are deleting one or multiple job items here we also need to
|
||||
// delete the attached messages.
|
||||
$query = new EntityFieldQuery();
|
||||
$result = $query->entityCondition('entity_type', 'tmgmt_message')
|
||||
->propertyCondition('tjiid', $ids)
|
||||
->execute();
|
||||
if (!empty($result['tmgmt_message'])) {
|
||||
$controller = entity_get_controller('tmgmt_message');
|
||||
// We need to directly query the entity controller so we can pass on
|
||||
// the transaction object.
|
||||
$controller->delete(array_keys($result['tmgmt_message']), $transaction);
|
||||
}
|
||||
|
||||
$query = new EntityFieldQuery();
|
||||
$result = $query->entityCondition('entity_type', 'tmgmt_remote')
|
||||
->propertyCondition('tjiid', $ids)
|
||||
->execute();
|
||||
if (!empty($result['tmgmt_remote'])) {
|
||||
$controller = entity_get_controller('tmgmt_remote');
|
||||
$controller->delete(array_keys($result['tmgmt_remote']), $transaction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invoke($hook, $entity) {
|
||||
// We need to check whether the state of the job is affected by this
|
||||
// deletion.
|
||||
if ($hook == 'delete' && $job = $entity->getJob()) {
|
||||
// We only care for active jobs.
|
||||
if ($job->isActive() && tmgmt_job_check_finished($job->tjid)) {
|
||||
// Mark the job as finished.
|
||||
$job->finished();
|
||||
}
|
||||
}
|
||||
parent::invoke($hook, $entity);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the remote controller class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller class for the remote job mapping entity.
|
||||
*
|
||||
* @ingroup tmgmt_job
|
||||
*/
|
||||
class TMGMTRemoteController extends EntityAPIController {
|
||||
|
||||
public function load($ids = array(), $conditions = array()) {
|
||||
$entities = parent::load($ids, $conditions);
|
||||
|
||||
foreach ($entities as &$entity) {
|
||||
if (is_string($entity->remote_data)) {
|
||||
$entity->remote_data = unserialize($entity->remote_data);
|
||||
}
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads remote mappings based on local data.
|
||||
*
|
||||
* @param int $tjid
|
||||
* Translation job id.
|
||||
* @param int $tjiid
|
||||
* Translation job item id.
|
||||
* @param int $data_item_key
|
||||
* Data item key.
|
||||
*
|
||||
* @return array
|
||||
* Array of TMGMTRemote entities.
|
||||
*/
|
||||
function loadByLocalData($tjid = NULL, $tjiid = NULL, $data_item_key = NULL) {
|
||||
$data_item_key = tmgmt_ensure_keys_string($data_item_key);
|
||||
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', 'tmgmt_remote');
|
||||
|
||||
if (!empty($tjid)) {
|
||||
$query->propertyCondition('tjid', $tjid);
|
||||
}
|
||||
if (!empty($tjiid)) {
|
||||
$query->propertyCondition('tjiid', $tjiid);
|
||||
}
|
||||
if (!empty($data_item_key)) {
|
||||
$query->propertyCondition('data_item_key', $data_item_key);
|
||||
}
|
||||
|
||||
$result = $query->execute();
|
||||
|
||||
if (isset($result['tmgmt_remote'])) {
|
||||
return entity_load('tmgmt_remote', array_keys($result['tmgmt_remote']));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads remote mapping entities based on remote identifier.
|
||||
*
|
||||
* @param int $remote_identifier_1
|
||||
* @param int $remote_identifier_2
|
||||
* @param int $remote_identifier_3
|
||||
*
|
||||
* @return array
|
||||
* Array of TMGMTRemote entities.
|
||||
*/
|
||||
function loadByRemoteIdentifier($remote_identifier_1 = NULL, $remote_identifier_2 = NULL, $remote_identifier_3 = NULL) {
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', 'tmgmt_remote');
|
||||
|
||||
if ($remote_identifier_1 !== NULL) {
|
||||
$query->propertyCondition('remote_identifier_1', $remote_identifier_1);
|
||||
}
|
||||
if ($remote_identifier_2 !== NULL) {
|
||||
$query->propertyCondition('remote_identifier_2', $remote_identifier_2);
|
||||
}
|
||||
if ($remote_identifier_3 !== NULL) {
|
||||
$query->propertyCondition('remote_identifier_3', $remote_identifier_3);
|
||||
}
|
||||
|
||||
$result = $query->execute();
|
||||
|
||||
if (isset($result['tmgmt_remote'])) {
|
||||
return entity_load('tmgmt_remote', array_keys($result['tmgmt_remote']));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the translator controller class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller class for the job entity.
|
||||
*
|
||||
* @ingroup tmgmt_translator
|
||||
*/
|
||||
class TMGMTTranslatorController extends EntityAPIControllerExportable {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
|
||||
$query = parent::buildQuery($ids, $conditions, $revision_id);
|
||||
if ($plugins = tmgmt_translator_plugin_info()) {
|
||||
$query->condition('plugin', array_keys($plugins));
|
||||
}
|
||||
else {
|
||||
// Don't return any translators if no plugin exists.
|
||||
$query->where('1 = 0');
|
||||
}
|
||||
// Sort by the weight of the translator.
|
||||
$query->orderBy('weight');
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($ids, DatabaseTransaction $transaction = NULL) {
|
||||
$cids = array();
|
||||
// We are never going to have many entities here, so we can risk a loop.
|
||||
foreach ($ids as $key => $name) {
|
||||
if (tmgmt_translator_busy($key)) {
|
||||
// The translator can't be deleted because it is currently busy. Remove
|
||||
// it from the ids so it wont get deleted in the parent implementation.
|
||||
unset($ids[$key]);
|
||||
}
|
||||
else {
|
||||
$cids[$key] = 'language:' . $key;
|
||||
}
|
||||
}
|
||||
// Clear the language cache for the deleted translators.
|
||||
cache_clear_all($cids, 'cache_tmgmt');
|
||||
parent::delete($ids, $transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($entity, DatabaseTransaction $transaction = NULL) {
|
||||
$return = parent::save($entity, $transaction);
|
||||
// Clear the languages cache.
|
||||
cache_clear_all('language:' . $entity->name, 'cache_tmgmt');
|
||||
return $return;
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
name = Translation Management Demo
|
||||
description = All elements for a functioning demo.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
|
||||
dependencies[] = tmgmt_ui
|
||||
dependencies[] = tmgmt_node_ui
|
||||
dependencies[] = tmgmt_file
|
||||
dependencies[] = tmgmt_local
|
||||
dependencies[] = google_chart_tools
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Installation hooks for tmgmt_demo module.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function tmgmt_demo_install() {
|
||||
include_once DRUPAL_ROOT . '/includes/locale.inc';
|
||||
// Add German to the language list.
|
||||
if (!array_key_exists('de', language_list())) {
|
||||
locale_add_language('de');
|
||||
}
|
||||
|
||||
// Add content type 'translatable'.
|
||||
if (!array_key_exists('translatable', node_type_get_names())) {
|
||||
$type = array(
|
||||
'type' => 'translatable',
|
||||
'name' => 'Translation Demo Type',
|
||||
'base' => 'node_content',
|
||||
'custom' => 1,
|
||||
'modified' => 1,
|
||||
'locked' => 0,
|
||||
);
|
||||
|
||||
$type = node_type_set_defaults($type);
|
||||
node_type_save($type);
|
||||
node_add_body_field($type);
|
||||
variable_set('language_content_type_translatable', TRUE);
|
||||
variable_set('comment_translatable', '0');
|
||||
}
|
||||
|
||||
// Add language skills to the admin user.
|
||||
$user = user_load(1);
|
||||
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills' => array(
|
||||
'und' => array(
|
||||
0 => array(
|
||||
'language_from' => 'de',
|
||||
'language_to' => 'en',
|
||||
),
|
||||
1 => array(
|
||||
'language_from' => 'en',
|
||||
'language_to' => 'de',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
user_save($user, $edit);
|
||||
|
||||
// Add demo content.
|
||||
$node = new stdClass();
|
||||
$node->title = 'Second node';
|
||||
$node->type = 'translatable';
|
||||
node_object_prepare($node);
|
||||
$node->language = 'en';
|
||||
$node->body[LANGUAGE_NONE][0]['value'] = 'Have another try. This text can be
|
||||
translated as well';
|
||||
$node->uid = $user->uid;
|
||||
node_save($node);
|
||||
|
||||
$node = new stdClass();
|
||||
$node->title = 'First node';
|
||||
$node->type = 'translatable';
|
||||
node_object_prepare($node);
|
||||
$node->language = 'en';
|
||||
$node->body[LANGUAGE_NONE][0]['value'] = 'This text can be translated with TMGMT.
|
||||
Use the "translate" Tab and choose "Request Translation" to get started';
|
||||
$node->uid = $user->uid;
|
||||
node_save($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function tmgmt_demo_uninstall() {
|
||||
// Remove the content type created by the demo module.
|
||||
if (array_key_exists('translatable', node_type_get_names())) {
|
||||
node_type_delete('translatable');
|
||||
variable_del('node_preview_translatable');
|
||||
node_types_rebuild();
|
||||
menu_rebuild();
|
||||
}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
<?php
|
@@ -0,0 +1,880 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains job entity class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Entity class for the tmgmt_job entity.
|
||||
*
|
||||
* @ingroup tmgmt_job
|
||||
*/
|
||||
class TMGMTJob extends Entity {
|
||||
|
||||
/**
|
||||
* Translation job identifier.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $tjid;
|
||||
|
||||
/**
|
||||
* A custom label for this job.
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* Current state of the translation job
|
||||
* @var type
|
||||
*/
|
||||
public $state;
|
||||
|
||||
/**
|
||||
* Language to be translated from.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $source_language;
|
||||
|
||||
/**
|
||||
* Language into which the data needs to be translated.
|
||||
*
|
||||
* @var varchar
|
||||
*/
|
||||
public $target_language;
|
||||
|
||||
/**
|
||||
* Reference to the used translator of this job.
|
||||
*
|
||||
* @see TMGMTJob::getTranslatorController()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $translator;
|
||||
|
||||
/**
|
||||
* Translator specific configuration and context information for this job.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $settings;
|
||||
|
||||
/**
|
||||
* Remote identification of this job.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $reference;
|
||||
|
||||
/**
|
||||
* The time when the job was created as a timestamp.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $created;
|
||||
|
||||
/**
|
||||
* The time when the job was changed as a timestamp.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $changed;
|
||||
|
||||
/**
|
||||
* The user id of the creator of the job.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $uid;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $values = array()) {
|
||||
parent::__construct($values, 'tmgmt_job');
|
||||
if (empty($this->tjid)) {
|
||||
$this->created = REQUEST_TIME;
|
||||
}
|
||||
if (!isset($this->state)) {
|
||||
$this->state = TMGMT_JOB_STATE_UNPROCESSED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones job as unprocessed.
|
||||
*/
|
||||
public function cloneAsUnprocessed() {
|
||||
$clone = clone $this;
|
||||
$clone->tjid = NULL;
|
||||
$clone->uid = NULL;
|
||||
$clone->changed = NULL;
|
||||
$clone->reference = NULL;
|
||||
$clone->created = REQUEST_TIME;
|
||||
$clone->state = TMGMT_JOB_STATE_UNPROCESSED;
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultLabel() {
|
||||
// In some cases we might have a user-defined label.
|
||||
if (!empty($this->label)) {
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
$items = $this->getItems();
|
||||
$count = count($items);
|
||||
if ($count > 0) {
|
||||
$source_label = reset($items)->getSourceLabel();
|
||||
$t_args = array('!title' => $source_label, '!more' => $count - 1);
|
||||
$label = format_plural($count, '!title', '!title and !more more', $t_args);
|
||||
|
||||
// If the label length exceeds maximum allowed then cut off exceeding
|
||||
// characters from the title and use it to recreate the label.
|
||||
if (strlen($label) > TMGMT_JOB_LABEL_MAX_LENGTH) {
|
||||
$max_length = strlen($source_label) - (strlen($label) - TMGMT_JOB_LABEL_MAX_LENGTH);
|
||||
$source_label = truncate_utf8($source_label, $max_length, TRUE);
|
||||
$t_args['!title'] = $source_label;
|
||||
$label = format_plural($count, '!title', '!title and !more more', $t_args);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$wrapper = entity_metadata_wrapper($this->entityType, $this);
|
||||
$source = $wrapper->source_language->label();
|
||||
if (empty($source)) {
|
||||
$source = '?';
|
||||
}
|
||||
$target = $wrapper->target_language->label();
|
||||
if (empty($target)) {
|
||||
$target = '?';
|
||||
}
|
||||
$label = t('From !source to !target', array('!source' => $source, '!target' => $target));
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultUri() {
|
||||
return array('path' => 'admin/tmgmt/jobs/' . $this->tjid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildContent($view_mode = 'full', $langcode = NULL) {
|
||||
$content = array();
|
||||
if (module_exists('tmgmt_ui')) {
|
||||
$content = entity_ui_get_form('tmgmt_job', $this);
|
||||
}
|
||||
return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the translation job.
|
||||
*
|
||||
* @param $plugin
|
||||
* The plugin name.
|
||||
* @param $item_type
|
||||
* The source item type.
|
||||
* @param $item_id
|
||||
* The source item id.
|
||||
*
|
||||
* @return TMGMTJobItem
|
||||
* The job item that was added to the job or FALSE if it couldn't be saved.
|
||||
* @throws TMGMTException
|
||||
* On zero item word count.
|
||||
*/
|
||||
public function addItem($plugin, $item_type, $item_id) {
|
||||
|
||||
$transaction = db_transaction();
|
||||
$is_new = FALSE;
|
||||
|
||||
if (empty($this->tjid)) {
|
||||
$this->save();
|
||||
$is_new = TRUE;
|
||||
}
|
||||
|
||||
$item = tmgmt_job_item_create($plugin, $item_type, $item_id, array('tjid' => $this->tjid));
|
||||
$item->save();
|
||||
|
||||
if ($item->getWordCount() == 0) {
|
||||
$transaction->rollback();
|
||||
|
||||
// In case we got word count 0 for the first job item, NULL tjid so that
|
||||
// if there is another addItem() call the rolled back job object will get
|
||||
// persisted.
|
||||
if ($is_new) {
|
||||
$this->tjid = NULL;
|
||||
}
|
||||
|
||||
throw new TMGMTException('Job item @label (@type) has no translatable content.',
|
||||
array('@label' => $item->label(), '@type' => $item->getSourceType()));
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a given TMGMTJobItem to this job.
|
||||
*
|
||||
* @param TMGMTJobItem $job
|
||||
* The job item to add.
|
||||
*/
|
||||
function addExistingItem(TMGMTJobItem &$item) {
|
||||
$item->tjid = $this->tjid;
|
||||
$item->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a log message for this job.
|
||||
*
|
||||
* @param $message
|
||||
* The message to store in the log. Keep $message translatable by not
|
||||
* concatenating dynamic values into it! Variables in the message should be
|
||||
* added by using placeholder strings alongside the variables argument to
|
||||
* declare the value of the placeholders. See t() for documentation on how
|
||||
* $message and $variables interact.
|
||||
* @param $variables
|
||||
* (Optional) An array of variables to replace in the message on display.
|
||||
* @param $type
|
||||
* (Optional) The type of the message. Can be one of 'status', 'error',
|
||||
* 'warning' or 'debug'. Messages of the type 'debug' will not get printed
|
||||
* to the screen.
|
||||
*/
|
||||
public function addMessage($message, $variables = array(), $type = 'status') {
|
||||
// Save the job if it hasn't yet been saved.
|
||||
if (!empty($this->tjid) || $this->save()) {
|
||||
$message = tmgmt_message_create($message, $variables, array(
|
||||
'tjid' => $this->tjid,
|
||||
'type' => $type,
|
||||
'uid' => $GLOBALS['user']->uid,
|
||||
));
|
||||
if ($message->save()) {
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all job items attached to this job.
|
||||
*
|
||||
* @param array $conditions
|
||||
* Additional conditions to pass into EFQ.
|
||||
*
|
||||
* @return TMGMTJobItem[]
|
||||
* An array of translation job items.
|
||||
*/
|
||||
public function getItems($conditions = array()) {
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', 'tmgmt_job_item');
|
||||
$query->propertyCondition('tjid', $this->tjid);
|
||||
foreach ($conditions as $key => $condition) {
|
||||
if (is_array($condition)) {
|
||||
$operator = isset($condition['operator']) ? $condition['operator'] : '=';
|
||||
$query->propertyCondition($key, $condition['value'], $operator);
|
||||
}
|
||||
else {
|
||||
$query->propertyCondition($key, $condition);
|
||||
}
|
||||
}
|
||||
$results = $query->execute();
|
||||
if (!empty($results['tmgmt_job_item'])) {
|
||||
return entity_load('tmgmt_job_item', array_keys($results['tmgmt_job_item']));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all job messages attached to this job.
|
||||
*
|
||||
* @return array
|
||||
* An array of translation job messages.
|
||||
*/
|
||||
public function getMessages($conditions = array()) {
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', 'tmgmt_message');
|
||||
$query->propertyCondition('tjid', $this->tjid);
|
||||
foreach ($conditions as $key => $condition) {
|
||||
if (is_array($condition)) {
|
||||
$operator = isset($condition['operator']) ? $condition['operator'] : '=';
|
||||
$query->propertyCondition($key, $condition['value'], $operator);
|
||||
}
|
||||
else {
|
||||
$query->propertyCondition($key, $condition);
|
||||
}
|
||||
}
|
||||
$results = $query->execute();
|
||||
if (!empty($results['tmgmt_message'])) {
|
||||
return entity_load('tmgmt_message', array_keys($results['tmgmt_message']));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all job messages attached to this job with timestamp newer than
|
||||
* $time.
|
||||
*
|
||||
* @param $time
|
||||
* (Optional) Messages need to have a newer timestamp than $time. Defaults
|
||||
* to REQUEST_TIME.
|
||||
*
|
||||
* @return array
|
||||
* An array of translation job messages.
|
||||
*/
|
||||
public function getMessagesSince($time = NULL) {
|
||||
$time = isset($time) ? $time : REQUEST_TIME;
|
||||
$conditions = array('created' => array('value' => $time, 'operator' => '>='));
|
||||
return $this->getMessages($conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a setting value from the job settings. Pulls the default values
|
||||
* (if defined) from the plugin controller.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the setting.
|
||||
*
|
||||
* @return
|
||||
* The setting value or $default if the setting value is not set. Returns
|
||||
* NULL if the setting does not exist at all.
|
||||
*/
|
||||
public function getSetting($name) {
|
||||
if (isset($this->settings[$name])) {
|
||||
return $this->settings[$name];
|
||||
}
|
||||
// The translator might provide default settings.
|
||||
if ($translator = $this->getTranslator()) {
|
||||
if (($setting = $translator->getSetting($name)) !== NULL) {
|
||||
return $setting;
|
||||
}
|
||||
}
|
||||
if ($controller = $this->getTranslatorController()) {
|
||||
$defaults = $controller->defaultSettings();
|
||||
if (isset($defaults[$name])) {
|
||||
return $defaults[$name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translator for this job.
|
||||
*
|
||||
* @return TMGMTTranslator
|
||||
* The translator entity or FALSE if there was a problem.
|
||||
*/
|
||||
public function getTranslator() {
|
||||
if (isset($this->translator)) {
|
||||
return tmgmt_translator_load($this->translator);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the job. Can be one of the job state constants.
|
||||
*
|
||||
* @return integer
|
||||
* The state of the job or NULL if it hasn't been set yet.
|
||||
*/
|
||||
public function getState() {
|
||||
// We don't need to check if the state is actually set because we always set
|
||||
// it in the constructor.
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state of the job.
|
||||
*
|
||||
* @param $state
|
||||
* The new state of the job. Has to be one of the job state constants.
|
||||
* @param $message
|
||||
* (Optional) The log message to be saved along with the state change.
|
||||
* @param $variables
|
||||
* (Optional) An array of variables to replace in the message on display.
|
||||
*
|
||||
* @return int
|
||||
* The updated state of the job if it could be set.
|
||||
*
|
||||
* @see TMGMTJob::addMessage()
|
||||
*/
|
||||
public function setState($state, $message = NULL, $variables = array(), $type = 'debug') {
|
||||
// Return TRUE if the state could be set. Return FALSE otherwise.
|
||||
if (array_key_exists($state, tmgmt_job_states())) {
|
||||
$this->state = $state;
|
||||
$this->save();
|
||||
// If a message is attached to this state change add it now.
|
||||
if (!empty($message)) {
|
||||
$this->addMessage($message, $variables, $type);
|
||||
}
|
||||
}
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the passed value matches the current state.
|
||||
*
|
||||
* @param $state
|
||||
* The value to check the current state against.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the passed state matches the current state, FALSE otherwise.
|
||||
*/
|
||||
public function isState($state) {
|
||||
return $this->getState() == $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the user described by $account is the author of this job.
|
||||
*
|
||||
* @param $account
|
||||
* (Optional) A user object. Defaults to the currently logged in user.
|
||||
*/
|
||||
public function isAuthor($account = NULL) {
|
||||
$account = isset($account) ? $account : $GLOBALS['user'];
|
||||
return $this->uid == $account->uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the state of this job is 'unprocessed'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the state is 'unprocessed', FALSE otherwise.
|
||||
*/
|
||||
public function isUnprocessed() {
|
||||
return $this->isState(TMGMT_JOB_STATE_UNPROCESSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the state of this job is 'aborted'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the state is 'aborted', FALSE otherwise.
|
||||
*/
|
||||
public function isAborted() {
|
||||
return $this->isState(TMGMT_JOB_STATE_ABORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the state of this job is 'active'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the state is 'active', FALSE otherwise.
|
||||
*/
|
||||
public function isActive() {
|
||||
return $this->isState(TMGMT_JOB_STATE_ACTIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the state of this job is 'rejected'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the state is 'rejected', FALSE otherwise.
|
||||
*/
|
||||
public function isRejected() {
|
||||
return $this->isState(TMGMT_JOB_STATE_REJECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the state of this jon is 'finished'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the state is 'finished', FALSE otherwise.
|
||||
*/
|
||||
public function isFinished() {
|
||||
return $this->isState(TMGMT_JOB_STATE_FINISHED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a job is translatable.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the job can be translated, FALSE otherwise.
|
||||
*/
|
||||
public function isTranslatable() {
|
||||
if ($translator = $this->getTranslator()) {
|
||||
if ($translator->canTranslate($this)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a job is abortable.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the job can be aborted, FALSE otherwise.
|
||||
*/
|
||||
public function isAbortable() {
|
||||
// Only non-submitted translation jobs can be aborted.
|
||||
return $this->isActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a job is submittable.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the job can be submitted, FALSE otherwise.
|
||||
*/
|
||||
public function isSubmittable() {
|
||||
return $this->isUnprocessed() || $this->isRejected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a job is deletable.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the job can be deleted, FALSE otherwise.
|
||||
*/
|
||||
public function isDeletable() {
|
||||
return !$this->isActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of the job to 'submitted'.
|
||||
*
|
||||
* @param $message
|
||||
* The log message to be saved along with the state change.
|
||||
* @param $variables
|
||||
* (Optional) An array of variables to replace in the message on display.
|
||||
*
|
||||
* @return TMGMTJob
|
||||
* The job entity.
|
||||
*
|
||||
* @see TMGMTJob::addMessage()
|
||||
*/
|
||||
public function submitted($message = NULL, $variables = array(), $type = 'status') {
|
||||
if (!isset($message)) {
|
||||
$message = 'The translation job has been submitted.';
|
||||
}
|
||||
$this->setState(TMGMT_JOB_STATE_ACTIVE, $message, $variables, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of the job to 'finished'.
|
||||
*
|
||||
* @param $message
|
||||
* The log message to be saved along with the state change.
|
||||
* @param $variables
|
||||
* (Optional) An array of variables to replace in the message on display.
|
||||
*
|
||||
* @return TMGMTJob
|
||||
* The job entity.
|
||||
*
|
||||
* @see TMGMTJob::addMessage()
|
||||
*/
|
||||
public function finished($message = NULL, $variables = array(), $type = 'status') {
|
||||
if (!isset($message)) {
|
||||
$message = 'The translation job has been finished.';
|
||||
}
|
||||
return $this->setState(TMGMT_JOB_STATE_FINISHED, $message, $variables, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of the job to 'aborted'.
|
||||
*
|
||||
* @param $message
|
||||
* The log message to be saved along with the state change.
|
||||
* @param $variables
|
||||
* (Optional) An array of variables to replace in the message on display.
|
||||
*
|
||||
* Use TMGMTJob::abortTranslation() to abort a translation.
|
||||
*
|
||||
* @return TMGMTJob
|
||||
* The job entity.
|
||||
*
|
||||
* @see TMGMTJob::addMessage()
|
||||
*/
|
||||
public function aborted($message = NULL, $variables = array(), $type = 'status') {
|
||||
if (!isset($message)) {
|
||||
$message = 'The translation job has been aborted.';
|
||||
}
|
||||
/** @var TMGMTJobItem $item */
|
||||
foreach ($this->getItems() as $item) {
|
||||
$item->setState(TMGMT_JOB_ITEM_STATE_ABORTED);
|
||||
}
|
||||
return $this->setState(TMGMT_JOB_STATE_ABORTED, $message, $variables, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of the job to 'rejected'.
|
||||
*
|
||||
* @param $message
|
||||
* The log message to be saved along with the state change.
|
||||
* @param $variables
|
||||
* (Optional) An array of variables to replace in the message on display.
|
||||
*
|
||||
* @return TMGMTJob
|
||||
* The job entity.
|
||||
*
|
||||
* @see TMGMTJob::addMessage()
|
||||
*/
|
||||
public function rejected($message = NULL, $variables = array(), $type = 'error') {
|
||||
if (!isset($message)) {
|
||||
$message = 'The translation job has been rejected by the translation provider.';
|
||||
}
|
||||
return $this->setState(TMGMT_JOB_STATE_REJECTED, $message, $variables, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the translation of a job from the translator.
|
||||
*
|
||||
* @return integer
|
||||
* The updated job status.
|
||||
*/
|
||||
public function requestTranslation() {
|
||||
if (!$this->isTranslatable() || !$controller = $this->getTranslatorController()) {
|
||||
return FALSE;
|
||||
}
|
||||
// We don't know if the translator plugin already processed our
|
||||
// translation request after this point. That means that the plugin has to
|
||||
// set the 'submitted', 'needs review', etc. states on its own.
|
||||
$controller->requestTranslation($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to abort the translation job. Already accepted jobs can not be
|
||||
* aborted, submitted jobs only if supported by the translator plugin.
|
||||
* Always use this method if you want to abort a translation job.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the translation job was aborted, FALSE otherwise.
|
||||
*/
|
||||
public function abortTranslation() {
|
||||
if (!$this->isAbortable() || !$controller = $this->getTranslatorController()) {
|
||||
return FALSE;
|
||||
}
|
||||
// We don't know if the translator plugin was able to abort the translation
|
||||
// job after this point. That means that the plugin has to set the
|
||||
// 'aborted' state on its own.
|
||||
return $controller->abortTranslation($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translator plugin controller of the translator of this job.
|
||||
*
|
||||
* @return TMGMTTranslatorPluginControllerInterface
|
||||
* The controller of the translator plugin.
|
||||
*/
|
||||
public function getTranslatorController() {
|
||||
if ($translator = $this->getTranslator($this)) {
|
||||
return $translator->getController();
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source data of all job items.
|
||||
*
|
||||
* @param $key
|
||||
* If present, only the subarray identified by key is returned.
|
||||
* @param $index
|
||||
* Optional index of an attribute below $key.
|
||||
* @return array
|
||||
* A nested array with the source data where the most upper key is the job
|
||||
* item id.
|
||||
*/
|
||||
public function getData(array $key = array(), $index = NULL) {
|
||||
$data = array();
|
||||
if (!empty($key)) {
|
||||
$tjiid = array_shift($key);
|
||||
$item = entity_load_single('tmgmt_job_item', $tjiid);
|
||||
if ($item) {
|
||||
$data[$tjiid] = $item->getData($key, $index);
|
||||
// If not set, use the job item label as the data label.
|
||||
if (!isset($data[$tjiid]['#label'])) {
|
||||
$data[$tjiid]['#label'] = $item->getSourceLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($this->getItems() as $tjiid => $item) {
|
||||
$data[$tjiid] = $item->getData();
|
||||
// If not set, use the job item label as the data label.
|
||||
if (!isset($data[$tjiid]['#label'])) {
|
||||
$data[$tjiid]['#label'] = $item->getSourceLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums up all pending counts of this jobs job items.
|
||||
*
|
||||
* @return
|
||||
* The sum of all pending counts
|
||||
*/
|
||||
public function getCountPending() {
|
||||
return tmgmt_job_statistic($this, 'count_pending');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums up all translated counts of this jobs job items.
|
||||
*
|
||||
* @return
|
||||
* The sum of all translated counts
|
||||
*/
|
||||
public function getCountTranslated() {
|
||||
return tmgmt_job_statistic($this, 'count_translated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums up all accepted counts of this jobs job items.
|
||||
*
|
||||
* @return
|
||||
* The sum of all accepted data items.
|
||||
*/
|
||||
public function getCountAccepted() {
|
||||
return tmgmt_job_statistic($this, 'count_accepted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums up all accepted counts of this jobs job items.
|
||||
*
|
||||
* @return
|
||||
* The sum of all accepted data items.
|
||||
*/
|
||||
public function getCountReviewed() {
|
||||
return tmgmt_job_statistic($this, 'count_reviewed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums up all word counts of this jobs job items.
|
||||
*
|
||||
* @return
|
||||
* The total word count of this job.
|
||||
*/
|
||||
public function getWordCount() {
|
||||
return tmgmt_job_statistic($this, 'word_count');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store translated data back into the items.
|
||||
*
|
||||
* @param $data
|
||||
* Partially or complete translated data, the most upper key needs to be
|
||||
* the translation job item id.
|
||||
* @param $key
|
||||
* (Optional) Either a flattened key (a 'key1][key2][key3' string) or a nested
|
||||
* one, e.g. array('key1', 'key2', 'key2'). Defaults to an empty array which
|
||||
* means that it will replace the whole translated data array. The most
|
||||
* upper key entry needs to be the job id (tjiid).
|
||||
*/
|
||||
public function addTranslatedData($data, $key = NULL) {
|
||||
$key = tmgmt_ensure_keys_array($key);
|
||||
$items = $this->getItems();
|
||||
// If there is a key, get the specific item and forward the call.
|
||||
if (!empty($key)) {
|
||||
$item_id = array_shift($key);
|
||||
if (isset($items[$item_id])) {
|
||||
$items[$item_id]->addTranslatedData($data, $key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($data as $key => $value) {
|
||||
if (isset($items[$key])) {
|
||||
$items[$key]->addTranslatedData($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates the returned job item translations to the sources.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if we were able to propagate the translated data, FALSE otherwise.
|
||||
*/
|
||||
public function acceptTranslation() {
|
||||
foreach ($this->getItems() as $item) {
|
||||
$item->acceptTranslation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets remote mappings for current job.
|
||||
*
|
||||
* @return array
|
||||
* List of TMGMTRemote entities.
|
||||
*/
|
||||
public function getRemoteMappings() {
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', 'tmgmt_remote');
|
||||
$query->propertyCondition('tjid', $this->tjid);
|
||||
$result = $query->execute();
|
||||
|
||||
if (isset($result['tmgmt_remote'])) {
|
||||
return entity_load('tmgmt_remote', array_keys($result['tmgmt_remote']));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the hook 'hook_tmgmt_source_suggestions' to get all suggestions.
|
||||
*
|
||||
* @param arary $conditions
|
||||
* Conditions to pass only some and not all items to the hook.
|
||||
*
|
||||
* @return array
|
||||
* An array with all additional translation suggestions.
|
||||
* - job_item: A TMGMTJobItem instance.
|
||||
* - referenced: A string which indicates where this suggestion comes from.
|
||||
* - from_job: The main TMGMTJob-ID which suggests this translation.
|
||||
*/
|
||||
public function getSuggestions(array $conditions = array()) {
|
||||
$suggestions = module_invoke_all('tmgmt_source_suggestions', $this->getItems($conditions), $this);
|
||||
|
||||
// Each TMGMTJob needs a job id to be able to count the words, because the
|
||||
// source-language is stored in the job and not the item.
|
||||
foreach ($suggestions as &$suggestion) {
|
||||
$jobItem = $suggestion['job_item'];
|
||||
$jobItem->tjid = $this->tjid;
|
||||
$jobItem->recalculateStatistics();
|
||||
}
|
||||
return $suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all suggestions from the given list which should not be processed.
|
||||
*
|
||||
* This function removes all suggestions from the given list which are already
|
||||
* assigned to a translation job or which should not be processed because
|
||||
* there are no words, no translation is needed, ...
|
||||
*
|
||||
* @param array &$suggestions
|
||||
* Associative array of translation suggestions. It must contain at least:
|
||||
* - tmgmt_job: An instance of a TMGMTJobItem.
|
||||
*/
|
||||
public function cleanSuggestionsList(array &$suggestions) {
|
||||
foreach ($suggestions as $k => $suggestion) {
|
||||
if (is_array($suggestion) && isset($suggestion['job_item']) && ($suggestion['job_item'] instanceof TMGMTJobItem)) {
|
||||
$jobItem = $suggestion['job_item'];
|
||||
|
||||
// Items with no words to translate should not be presented.
|
||||
if ($jobItem->getWordCount() <= 0) {
|
||||
unset($suggestions[$k]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if there already exists a translation job for this item in the
|
||||
// current language.
|
||||
$items = tmgmt_job_item_load_all_latest($jobItem->plugin, $jobItem->item_type, $jobItem->item_id, $this->source_language);
|
||||
if ($items && isset($items[$this->target_language])) {
|
||||
unset($suggestions[$k]);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
unset($suggestions[$k]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains message entity class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Entity class for the tmgmt_message entity.
|
||||
*
|
||||
* @ingroup tmgmt_job
|
||||
*/
|
||||
class TMGMTMessage extends Entity {
|
||||
|
||||
/**
|
||||
* The ID of the message..
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $mid;
|
||||
|
||||
/**
|
||||
* The ID of the job.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $tjid;
|
||||
|
||||
/**
|
||||
* The ID of the job item.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $tjiid;
|
||||
|
||||
/**
|
||||
* User uid.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $uid;
|
||||
|
||||
/**
|
||||
* The message text.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $message;
|
||||
|
||||
/**
|
||||
* An array of string replacement arguments as used by t().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $variables;
|
||||
|
||||
/**
|
||||
* The time when the message object was created as a timestamp.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $created;
|
||||
|
||||
/**
|
||||
* Type of the message (debug, status, warning or error).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $values = array()) {
|
||||
parent::__construct($values, 'tmgmt_message');
|
||||
if (empty($this->created)) {
|
||||
$this->created = REQUEST_TIME;
|
||||
}
|
||||
if (empty($this->type)) {
|
||||
$this->type = 'status';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultLabel() {
|
||||
$created = format_date($this->created);
|
||||
switch ($this->type) {
|
||||
case 'error':
|
||||
return t('Error message from @time', array('@time' => $created));
|
||||
case 'status':
|
||||
return t('Status message from @time', array('@time' => $created));
|
||||
case 'warning':
|
||||
return t('Warning message from @time', array('@time' => $created));
|
||||
case 'debug':
|
||||
return t('Debug message from @time', array('@time' => $created));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translated message.
|
||||
*
|
||||
* @return
|
||||
* The translated message.
|
||||
*/
|
||||
public function getMessage() {
|
||||
$text = $this->message;
|
||||
if (is_array($this->variables) && !empty($this->variables)) {
|
||||
$text = t($text, $this->variables);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the job entity that this job message is attached to.
|
||||
*
|
||||
* @return TMGMTJob
|
||||
* The job entity that this job message is attached to or FALSE if there was
|
||||
* a problem.
|
||||
*/
|
||||
public function getJob() {
|
||||
if (!empty($this->tjid)) {
|
||||
return tmgmt_job_load($this->tjid);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the job entity that this job message is attached to.
|
||||
*
|
||||
* @return TMGMTJobItem
|
||||
* The job item entity that this job message is attached to or FALSE if
|
||||
* there was a problem.
|
||||
*/
|
||||
public function getJobItem() {
|
||||
if (!empty($this->tjiid)) {
|
||||
return tmgmt_job_item_load($this->tjiid);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains remote entity class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Entity class for the tmgmt_remote entity.
|
||||
*
|
||||
* @ingroup tmgmt_job
|
||||
*/
|
||||
class TMGMTRemote extends Entity {
|
||||
|
||||
/**
|
||||
* Primary key.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $trid;
|
||||
|
||||
/**
|
||||
* TMGMTJob identifier.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $tjid;
|
||||
|
||||
/**
|
||||
* TMGMTJobItem identifier.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $tjiid;
|
||||
|
||||
/**
|
||||
* Translation job data item key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $data_item_key;
|
||||
|
||||
/**
|
||||
* Custom remote identifier 1.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $remote_identifier_1;
|
||||
|
||||
/**
|
||||
* Custom remote identifier 2.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $remote_identifier_2;
|
||||
|
||||
/**
|
||||
* Custom remote identifier 3.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $remote_identifier_3;
|
||||
|
||||
/**
|
||||
* Remote job url.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $remote_url;
|
||||
|
||||
/**
|
||||
* Word count provided by the remote service.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $word_count;
|
||||
|
||||
/**
|
||||
* Amount charged for the remote translation job.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $amount;
|
||||
|
||||
/**
|
||||
* Amount charged currency.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $currency;
|
||||
|
||||
/**
|
||||
* Custom remote data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $remote_data;
|
||||
|
||||
|
||||
/**
|
||||
* Gets translation job.
|
||||
*
|
||||
* @return TMGMTJob
|
||||
*/
|
||||
function getJob() {
|
||||
return tmgmt_job_load($this->tjid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets translation job item.
|
||||
*
|
||||
* @return TMGMTJobItem
|
||||
*/
|
||||
function getJobItem() {
|
||||
if (!empty($this->tjiid)) {
|
||||
return tmgmt_job_item_load($this->tjiid);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds data to the remote_data storage.
|
||||
*
|
||||
* @param string $key
|
||||
* Key through which the data will be accessible.
|
||||
* @param $value
|
||||
* Value to store.
|
||||
*/
|
||||
function addRemoteData($key, $value) {
|
||||
$this->remote_data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data from remote_data storage.
|
||||
*
|
||||
* @param string $key
|
||||
* Access key for the data.
|
||||
*
|
||||
* @return mixed
|
||||
* Stored data.
|
||||
*/
|
||||
function getRemoteData($key) {
|
||||
return $this->remote_data[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes data from remote_data storage.
|
||||
*
|
||||
* @param string $key
|
||||
* Access key for the data that are to be removed.
|
||||
*/
|
||||
function removeRemoteData($key) {
|
||||
unset($this->remote_data[$key]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains translator entity class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Entity class for the tmgmt_translator entity.
|
||||
*
|
||||
* @ingroup tmgmt_translator
|
||||
*/
|
||||
class TMGMTTranslator extends Entity {
|
||||
|
||||
/**
|
||||
* The ID of the translator.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $tid;
|
||||
|
||||
/**
|
||||
* Machine readable name of the translator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Label of the translator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* Description of the translator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* Weight of the translator.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $weight;
|
||||
|
||||
/**
|
||||
* Plugin name of the translator.
|
||||
*
|
||||
* @type string
|
||||
*/
|
||||
public $plugin;
|
||||
|
||||
/**
|
||||
* Translator type specific settings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $settings;
|
||||
|
||||
/**
|
||||
* The supported target languages caches.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $languageCache;
|
||||
|
||||
/**
|
||||
* The supported language pairs caches.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $languagePairsCache;
|
||||
|
||||
/**
|
||||
* Whether the language cache in the database is outdated.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $languageCacheOutdated;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $values = array()) {
|
||||
parent::__construct($values, 'tmgmt_translator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translator plugin controller of this translator.
|
||||
*
|
||||
* @return TMGMTTranslatorPluginControllerInterface
|
||||
*/
|
||||
public function getController() {
|
||||
if (!empty($this->plugin)) {
|
||||
return tmgmt_translator_plugin_controller($this->plugin);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported target languages for this translator.
|
||||
*
|
||||
* @return array
|
||||
* An array of supported target languages in ISO format.
|
||||
*/
|
||||
public function getSupportedTargetLanguages($source_language) {
|
||||
if ($controller = $this->getController()) {
|
||||
if (isset($this->pluginInfo['cache languages']) && empty($this->pluginInfo['cache languages'])) {
|
||||
// This plugin doesn't support language caching.
|
||||
return $controller->getSupportedTargetLanguages($this, $source_language);
|
||||
}
|
||||
else {
|
||||
// Retrieve the supported languages from the cache.
|
||||
if (empty($this->languageCache) && $cache = cache_get('languages:' . $this->name, 'cache_tmgmt')) {
|
||||
$this->languageCache = $cache->data;
|
||||
}
|
||||
// Even if we successfully queried the cache it might not have an entry
|
||||
// for our source language yet.
|
||||
if (!isset($this->languageCache[$source_language])) {
|
||||
$this->languageCache[$source_language] = $controller->getSupportedTargetLanguages($this, $source_language);
|
||||
$this->languageCacheOutdated = TRUE;
|
||||
}
|
||||
}
|
||||
return $this->languageCache[$source_language];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the supported language pairs for this translator.
|
||||
*
|
||||
* @return array
|
||||
* List of language pairs where a pair is an associative array of
|
||||
* source_language and target_language.
|
||||
* Example:
|
||||
* array(
|
||||
* array('source_language' => 'en-US', 'target_language' => 'de-DE'),
|
||||
* array('source_language' => 'en-US', 'target_language' => 'de-CH'),
|
||||
* )
|
||||
*/
|
||||
public function getSupportedLanguagePairs() {
|
||||
if ($controller = $this->getController()) {
|
||||
if (isset($this->pluginInfo['cache languages']) && empty($this->pluginInfo['cache languages'])) {
|
||||
// This plugin doesn't support language caching.
|
||||
return $controller->getSupportedLanguagePairs($this);
|
||||
}
|
||||
else {
|
||||
// Retrieve the supported languages from the cache.
|
||||
if (empty($this->languagePairsCache) && $cache = cache_get('language_pairs:' . $this->name, 'cache_tmgmt')) {
|
||||
$this->languagePairsCache = $cache->data;
|
||||
}
|
||||
// Even if we successfully queried the cache data might not be yet
|
||||
// available.
|
||||
if (empty($this->languagePairsCache)) {
|
||||
$this->languagePairsCache = $controller->getSupportedLanguagePairs($this);
|
||||
$this->languageCacheOutdated = TRUE;
|
||||
}
|
||||
}
|
||||
return $this->languagePairsCache;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the language cache for this translator.
|
||||
*/
|
||||
public function clearLanguageCache() {
|
||||
cache_clear_all('languages:' . $this->name, 'cache_tmgmt');
|
||||
cache_clear_all('language_pairs:' . $this->name, 'cache_tmgmt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this translator can handle a particular translation job.
|
||||
*
|
||||
* @param $job
|
||||
* The TMGMTJob entity that should be translated.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the job can be processed and translated, FALSE otherwise.
|
||||
*/
|
||||
public function canTranslate(TMGMTJob $job) {
|
||||
if ($controller = $this->getController()) {
|
||||
return $controller->canTranslate($this, $job);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a translator is available.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the translator plugin is available, FALSE otherwise.
|
||||
*/
|
||||
public function isAvailable() {
|
||||
if ($controller = $this->getController()) {
|
||||
return $controller->isAvailable($this);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the plugin has any settings for this job.
|
||||
*/
|
||||
public function hasCheckoutSettings(TMGMTJob $job) {
|
||||
if ($controller = $this->getController()) {
|
||||
return $controller->hasCheckoutSettings($job);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Remove this once http://drupal.org/node/1420364 is done.
|
||||
*/
|
||||
public function getNotAvailableReason() {
|
||||
if ($controller = $this->getController()) {
|
||||
return $controller->getNotAvailableReason($this);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Remove this once http://drupal.org/node/1420364 is done.
|
||||
*/
|
||||
public function getNotCanTranslateReason(TMGMTJob $job) {
|
||||
if ($controller = $this->getController()) {
|
||||
return $controller->getNotCanTranslateReason($job);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a setting value from the translator settings. Pulls the default
|
||||
* values (if defined) from the plugin controller.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the setting.
|
||||
*
|
||||
* @return
|
||||
* The setting value or $default if the setting value is not set. Returns
|
||||
* NULL if the setting does not exist at all.
|
||||
*/
|
||||
public function getSetting($name) {
|
||||
if (isset($this->settings[$name])) {
|
||||
return $this->settings[$name];
|
||||
}
|
||||
elseif ($controller = $this->getController()) {
|
||||
$defaults = $controller->defaultSettings();
|
||||
if (isset($defaults[$name])) {
|
||||
return $defaults[$name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps local language to remote language.
|
||||
*
|
||||
* @param $language
|
||||
* Local language code.
|
||||
*
|
||||
* @return string
|
||||
* Remote language code.
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function mapToRemoteLanguage($language) {
|
||||
return $this->getController()->mapToRemoteLanguage($this, $language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps remote language to local language.
|
||||
*
|
||||
* @param $language
|
||||
* Remote language code.
|
||||
*
|
||||
* @return string
|
||||
* Local language code.
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function mapToLocalLanguage($language) {
|
||||
return $this->getController()->mapToLocalLanguage($this, $language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the language cache if it has changed.
|
||||
*/
|
||||
public function __destruct() {
|
||||
if ($controller = $this->getController()) {
|
||||
$info = $controller->pluginInfo();
|
||||
if (!isset($info['language cache']) || !empty($info['language cache']) && !empty($this->languageCacheOutdated)) {
|
||||
cache_set('languages:' . $this->name, $this->languageCache, 'cache_tmgmt');
|
||||
cache_set('language_pairs:' . $this->name, $this->languagePairsCache, 'cache_tmgmt');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* TMGMT Exception class
|
||||
*/
|
||||
class TMGMTException extends Exception {
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array $data
|
||||
* Associative array of dynamic data that will be inserted into $message.
|
||||
* @param int $code
|
||||
*/
|
||||
function __construct($message = "", $data = array(), $code = 0) {
|
||||
parent::__construct(strtr($message, $data), $code);
|
||||
}
|
||||
}
|
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the metadata controller classes for the Translation Management Tool
|
||||
* entities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Metadata controller for the job entity.
|
||||
*/
|
||||
class TMGMTJobMetadataController extends EntityDefaultMetadataController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityPropertyInfo() {
|
||||
$info = parent::entityPropertyInfo();
|
||||
$info = _tmgmt_override_property_description($info, $this->type);
|
||||
$properties = &$info[$this->type]['properties'];
|
||||
|
||||
// Make the created and changed property appear as date.
|
||||
$properties['changed']['type'] = $properties['created']['type'] = 'date';
|
||||
|
||||
// Use the defined entity label callback instead of the custom label directly.
|
||||
$properties['label']['getter callback'] = 'entity_class_label';
|
||||
|
||||
// Allow to change the properties.
|
||||
foreach (array('target_language', 'source_language', 'translator') as $property) {
|
||||
$properties[$property]['setter callback'] = 'entity_property_verbatim_set';
|
||||
}
|
||||
|
||||
// Add the options list for the available languages.
|
||||
$properties['target_language']['options list'] = $properties['source_language']['options list'] = 'entity_metadata_language_list';
|
||||
|
||||
// Add the options list for the defined state constants.
|
||||
$properties['state']['options list'] = 'tmgmt_job_states';
|
||||
|
||||
// Add the options list for all available translator plugins.
|
||||
$properties['translator']['type'] = 'tmgmt_translator';
|
||||
$properties['translator']['options list'] = 'tmgmt_translator_labels';
|
||||
|
||||
// Link the author property to the corresponding user entity.
|
||||
$properties['author'] = array(
|
||||
'label' => t('Author'),
|
||||
'type' => 'user',
|
||||
'description' => t('The author of the translation job.'),
|
||||
'setter callback' => 'entity_property_verbatim_set',
|
||||
'setter permission' => 'administer tmgmt',
|
||||
'required' => TRUE,
|
||||
'schema field' => 'uid',
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata controller for the job item entity.
|
||||
*/
|
||||
class TMGMTJobItemMetadataController extends EntityDefaultMetadataController {
|
||||
|
||||
public function entityPropertyInfo() {
|
||||
$info = parent::entityPropertyInfo();
|
||||
$info = _tmgmt_override_property_description($info, $this->type);
|
||||
$properties = &$info[$this->type]['properties'];
|
||||
|
||||
// Make the created and changed property appear as date.
|
||||
$properties['changed']['type'] = 'date';
|
||||
|
||||
// Add the options list for the defined state constants.
|
||||
$properties['state']['options list'] = 'tmgmt_job_item_states';
|
||||
|
||||
// Link the job id property to the corresponding job entity.
|
||||
$properties['tjid'] = array(
|
||||
'description' => t('Corresponding job entity.'),
|
||||
'type' => 'tmgmt_job',
|
||||
) + $properties['tjid'];
|
||||
|
||||
// Add the options list for all available source plugins.
|
||||
$properties['plugin']['options list'] = 'tmgmt_source_plugin_labels';
|
||||
|
||||
$properties['word_count']['label'] = t('Word count');
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata controller for the job message entity.
|
||||
*/
|
||||
class TMGMTMessageMetadataController extends EntityDefaultMetadataController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityPropertyInfo() {
|
||||
$info = parent::entityPropertyInfo();
|
||||
$info = _tmgmt_override_property_description($info, $this->type);
|
||||
$properties = &$info[$this->type]['properties'];
|
||||
|
||||
// Make the created property appear as date.
|
||||
$properties['created']['type'] = 'date';
|
||||
|
||||
// Link the job id property to the corresponding job entity.
|
||||
$properties['tjid'] = array(
|
||||
'description' => t('Corresponding job entity.'),
|
||||
'type' => 'tmgmt_job',
|
||||
) + $properties['tjid'];
|
||||
|
||||
// Link the job item id property to the corresponding job item entity.
|
||||
$properties['tjiid'] = array(
|
||||
'description' => t('Corresponding job item entity.'),
|
||||
'type' => 'tmgmt_job_item',
|
||||
) + $properties['tjiid'];
|
||||
|
||||
// Link user, was added in an update so make sure that it doesn't explode
|
||||
// if the schema cache was not cleared.
|
||||
$properties['uid'] = array(
|
||||
'type' => 'user',
|
||||
'description' => t('User associated with TMGMT Job Message entity.'),
|
||||
) + (isset($properties['uid']) ? $properties['uid'] : array());
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata controller for the translator entity.
|
||||
*/
|
||||
class TMGMTTranslatorMetadataController extends EntityDefaultMetadataController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityPropertyInfo() {
|
||||
$info = parent::entityPropertyInfo();
|
||||
$info = _tmgmt_override_property_description($info, $this->type);
|
||||
$properties = &$info[$this->type]['properties'];
|
||||
|
||||
// Options list callback for the translator plugin labels.
|
||||
$properties['plugin']['options list'] = 'tmgmt_translator_plugin_labels';
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates all entity property descriptions based on the schema definition.
|
||||
*
|
||||
* @param $info
|
||||
* Entity propety info array.
|
||||
*
|
||||
* @return
|
||||
* The altered entity properties array.
|
||||
*/
|
||||
function _tmgmt_override_property_description($info, $entity_type) {
|
||||
// Load tmgmt.install so we can access the schema.
|
||||
module_load_install('tmgmt');
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
$schema = tmgmt_schema();
|
||||
$fields = $schema[$entity_info['base table']]['fields'];
|
||||
$properties = &$info[$entity_type]['properties'];
|
||||
foreach ($properties as $name => $property_info) {
|
||||
if (isset($fields[$name]['description'])) {
|
||||
$properties[$name]['description'] = $fields[$name]['description'];
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the base plugin class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for Translation Management plugins.
|
||||
*/
|
||||
class TMGMTPluginBase implements TMGMTPluginBaseInterface {
|
||||
|
||||
protected $pluginType;
|
||||
protected $pluginInfo;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($type, $plugin) {
|
||||
$this->pluginType = $plugin;
|
||||
$this->pluginInfo = _tmgmt_plugin_info($type, $plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function pluginInfo() {
|
||||
return $this->pluginInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function pluginType() {
|
||||
return $this->pluginType;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the base plugin interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base interface for Translation Management plugins.
|
||||
*/
|
||||
interface TMGMTPluginBaseInterface {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param $type
|
||||
* The plugin type.
|
||||
* @param $plugin
|
||||
* The machine-readable name of the plugin.
|
||||
*/
|
||||
public function __construct($type, $plugin);
|
||||
|
||||
/**
|
||||
* Returns the info of the type of the plugin.
|
||||
*
|
||||
* @see tmgmt_source_plugin_info()
|
||||
*/
|
||||
public function pluginInfo();
|
||||
|
||||
/**
|
||||
* Returns the type of the plugin.
|
||||
*/
|
||||
public function pluginType();
|
||||
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the reject translator plugin interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handle reject on data item level.
|
||||
*
|
||||
* Implement this interface in a translator plugin to signal that this plugin is
|
||||
* capable of handling a reject of single data items.
|
||||
*
|
||||
* @ingroup tmgmt_translator
|
||||
*/
|
||||
interface TMGMTTranslatorRejectDataItem {
|
||||
|
||||
/**
|
||||
* Reject one single data item.
|
||||
*
|
||||
* @todo Using job item breaks the current convention which uses jobs.
|
||||
*
|
||||
* @param $job_item
|
||||
* The job item to which the rejected data item belongs.
|
||||
* @param $key
|
||||
* The key of the rejected data item.
|
||||
* The key is an array containing the keys of a nested array hierarchy path.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the reject was succesfull, else FALSE.
|
||||
* In case of an error, it is the responsibility of the translator to
|
||||
* provide informations about the faliure.
|
||||
*/
|
||||
public function rejectDataItem(TMGMTJobItem $job_item, array $key, array $values = NULL);
|
||||
|
||||
/**
|
||||
* Reject form.
|
||||
*
|
||||
* This method gets call by tmgmt_ui_translation_review_form_reject_confirm
|
||||
* and allows the translator to add aditional form elements in order to
|
||||
* collect data needed for the reject prozess.
|
||||
*
|
||||
* @param $form
|
||||
* The form array containing a confirm form.
|
||||
* $form['item'] holds the job item to which the to be rejected data item
|
||||
* belongs to.
|
||||
* $form['item'] holds key of the to be rejected data item as an array of
|
||||
* keys of a nested array hierarchy.
|
||||
* @param $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return
|
||||
* The resulting form array.
|
||||
*/
|
||||
public function rejectForm($form, &$form_state);
|
||||
}
|
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the source plugin interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for source plugin controllers.
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
interface TMGMTSourcePluginControllerInterface extends TMGMTPluginBaseInterface {
|
||||
|
||||
/**
|
||||
* Returns an array with the data structured for translation.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
* The job item entity.
|
||||
*
|
||||
* @see TMGMTJobItem::getData()
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item);
|
||||
|
||||
/**
|
||||
* Saves a translation.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
* The job item entity.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the translation was saved successfully, FALSE otherwise.
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item);
|
||||
|
||||
/**
|
||||
* Return a title for this job item.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
* The job item entity.
|
||||
*/
|
||||
public function getLabel(TMGMTJobItem $job_item);
|
||||
|
||||
/**
|
||||
* Returns the Uri for this job item.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
* The job item entity.
|
||||
*
|
||||
* @see entity_uri()
|
||||
*/
|
||||
public function getUri(TMGMTJobItem $job_item);
|
||||
|
||||
/**
|
||||
* Returns an array of translatable source item types.
|
||||
*/
|
||||
public function getItemTypes();
|
||||
|
||||
/**
|
||||
* Returns the label of a source item type.
|
||||
*
|
||||
* @param $type
|
||||
* The identifier of a source item type.
|
||||
*/
|
||||
public function getItemTypeLabel($type);
|
||||
|
||||
/**
|
||||
* Returns the type of a job item.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
* The job item.
|
||||
*
|
||||
* @return string
|
||||
* A type that describes the job item.
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item);
|
||||
|
||||
/**
|
||||
* Gets language code of the job item source.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
* The job item.
|
||||
*
|
||||
* @return string
|
||||
* Language code.
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item);
|
||||
|
||||
/**
|
||||
* Gets existing translation language codes of the job item source.
|
||||
*
|
||||
* Returns language codes that can be used as the source language for a
|
||||
* translation job.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
* The job item.
|
||||
*
|
||||
* @return array
|
||||
* Array of language codes.
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item);
|
||||
|
||||
}
|
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the source plugin interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for service plugin controllers.
|
||||
*
|
||||
* @ingroup tmgmt_translator
|
||||
*/
|
||||
interface TMGMTTranslatorPluginControllerInterface extends TMGMTPluginBaseInterface {
|
||||
|
||||
/**
|
||||
* Checks whether a translator is available.
|
||||
*
|
||||
* @param TMGMTTranslator $translator
|
||||
* The translator entity.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the translator plugin is available, FALSE otherwise.
|
||||
*/
|
||||
public function isAvailable(TMGMTTranslator $translator);
|
||||
|
||||
/**
|
||||
* Return a reason why the translator is not available.
|
||||
*
|
||||
* @param TMGMTTranslator $translator
|
||||
* The translator entity.
|
||||
*
|
||||
* Might be called when isAvailable() returns FALSE to get a reason that
|
||||
* can be displayed to the user.
|
||||
*
|
||||
* @todo Remove this once http://drupal.org/node/1420364 is done.
|
||||
*/
|
||||
public function getNotAvailableReason(TMGMTTranslator $translator);
|
||||
|
||||
/**
|
||||
* Check whether this service can handle a particular translation job.
|
||||
*
|
||||
* @param TMGMTTranslator $translator
|
||||
* The TMGMTTranslator entity that should handle the translation.
|
||||
* @param TMGMTJob $job
|
||||
* The TMGMTJob entity that should be translated.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the job can be processed and translated, FALSE otherwise.
|
||||
*/
|
||||
public function canTranslate(TMGMTTranslator $translator, TMGMTJob $job);
|
||||
|
||||
/**
|
||||
* Return a reason why the translator is not able to translate this job.
|
||||
*
|
||||
* @param TMGMTJob $job
|
||||
* The job entity.
|
||||
*
|
||||
* Might be called when canTranslate() returns FALSE to get a reason that
|
||||
* can be displayed to the user.
|
||||
*
|
||||
* @todo Remove this once http://drupal.org/node/1420364 is done.
|
||||
*/
|
||||
public function getNotCanTranslateReason(TMGMTJob $job);
|
||||
|
||||
/**
|
||||
* Specifies default mappings for local to remote language codes.
|
||||
*
|
||||
* This method can be used in case we know in advance what language codes are
|
||||
* used by the remote translator and to which local language codes they
|
||||
* correspond.
|
||||
*
|
||||
* @return array
|
||||
* An array of local => remote language codes.
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function getDefaultRemoteLanguagesMappings();
|
||||
|
||||
/**
|
||||
* Gets all supported languages of the translator.
|
||||
*
|
||||
* This list of all language codes used by the remote translator is then used
|
||||
* for example in the translator settings form to select which remote language
|
||||
* code correspond to which local language code.
|
||||
*
|
||||
* @param TMGMTTranslator $translator
|
||||
* Translator entity for which to get supported languages.
|
||||
*
|
||||
* @return array
|
||||
* An array of language codes which are provided by the translator
|
||||
* (remote language codes).
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function getSupportedRemoteLanguages(TMGMTTranslator $translator);
|
||||
|
||||
/**
|
||||
* Gets existing remote languages mappings.
|
||||
*
|
||||
* This method is responsible to provide all local to remote language pairs.
|
||||
*
|
||||
* @param TMGMTTranslator $translator
|
||||
* Translator entity for which to get mappings.
|
||||
*
|
||||
* @return array
|
||||
* An array of local => remote language codes.
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function getRemoteLanguagesMappings(TMGMTTranslator $translator);
|
||||
|
||||
/**
|
||||
* Maps local language to remote language.
|
||||
*
|
||||
* @param TMGMTTranslator $translator
|
||||
* Translator entity for which to get remote language.
|
||||
* @param $language
|
||||
* Local language code.
|
||||
*
|
||||
* @return string
|
||||
* Remote language code.
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function mapToRemoteLanguage(TMGMTTranslator $translator, $language);
|
||||
|
||||
/**
|
||||
* Maps remote language to local language.
|
||||
*
|
||||
* @param TMGMTTranslator $translator
|
||||
* Translator entity for which to get local language.
|
||||
* @param $language
|
||||
* Remote language code.
|
||||
*
|
||||
* @return string
|
||||
* Local language code.
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function mapToLocalLanguage(TMGMTTranslator $translator, $language);
|
||||
|
||||
/**
|
||||
* Returns all available target languages that are supported by this service
|
||||
* when given a source language.
|
||||
*
|
||||
* @param TMGMTTranslator $translator
|
||||
* The translator entity.
|
||||
* @param $source_language
|
||||
* The source language.
|
||||
*
|
||||
* @return array
|
||||
* An array of remote languages in ISO format.
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function getSupportedTargetLanguages(TMGMTTranslator $translator, $source_language);
|
||||
|
||||
/**
|
||||
* Returns supported language pairs.
|
||||
*
|
||||
* This info may be used by other plugins to find out what language pairs
|
||||
* can handle the translator.
|
||||
*
|
||||
* @param TMGMTTranslator $translator
|
||||
* The translator entity.
|
||||
*
|
||||
* @return array
|
||||
* List of language pairs where a pair is an associative array of
|
||||
* source_language and target_language.
|
||||
* Example:
|
||||
* array(
|
||||
* array('source_language' => 'en-US', 'target_language' => 'de-DE'),
|
||||
* array('source_language' => 'en-US', 'target_language' => 'de-CH'),
|
||||
* )
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function getSupportedLanguagePairs(TMGMTTranslator $translator);
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
*
|
||||
* Submits the translation request and sends it to the translation provider.
|
||||
*
|
||||
* @param TMGMTJob $job
|
||||
* The job that should be submitted.
|
||||
*
|
||||
* @ingroup tmgmt_remote_languages_mapping
|
||||
*/
|
||||
public function requestTranslation(TMGMTJob $job);
|
||||
|
||||
/**
|
||||
* Aborts a translation job.
|
||||
*
|
||||
* @param TMGMTJob $job
|
||||
* The job that should have its translation aborted.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the job could be aborted, FALSE otherwise.
|
||||
*/
|
||||
public function abortTranslation(TMGMTJob $job);
|
||||
|
||||
/**
|
||||
* Defines default settings.
|
||||
*
|
||||
* @return array
|
||||
* An array of default settings.
|
||||
*/
|
||||
public function defaultSettings();
|
||||
|
||||
/**
|
||||
* Returns if the translator has any settings for the passed job.
|
||||
*/
|
||||
public function hasCheckoutSettings(TMGMTJob $job);
|
||||
|
||||
/**
|
||||
* Accept a single data item.
|
||||
*
|
||||
* @todo Using job item breaks the current convention which uses jobs.
|
||||
*
|
||||
* @param $job_item
|
||||
* The Job item the accepted data item belongs to.
|
||||
* @param $key
|
||||
* The key of the accepted data item.
|
||||
* The key is an array containing the keys of a nested array hierarchy path.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the approving was succesfull, FALSE otherwise.
|
||||
* In case of an error, it is the responsibility of the translator to
|
||||
* provide informations about the failure by adding a message to the job
|
||||
* item.
|
||||
*/
|
||||
public function acceptetDataItem(TMGMTJobItem $job_item, array $key);
|
||||
|
||||
/**
|
||||
* Returns the escaped #text of a data item.
|
||||
*
|
||||
* @param array $data_item
|
||||
* A data item with a #text and optional #escape definitions.
|
||||
*
|
||||
* @return string
|
||||
* The text of the data item with translator-specific escape patterns
|
||||
* applied.
|
||||
*/
|
||||
public function escapeText(array $data_item);
|
||||
|
||||
/**
|
||||
* Removes escape patterns from an escaped text.
|
||||
*
|
||||
* @param string $text
|
||||
* The text from which escape patterns should be removed.
|
||||
*
|
||||
* @return string
|
||||
* The unescaped text.
|
||||
*/
|
||||
public function unescapeText($text);
|
||||
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the abstract source base plugin class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default controller class for source plugins.
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
abstract class TMGMTDefaultSourcePluginController extends TMGMTPluginBase implements TMGMTSourcePluginControllerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
return t('@plugin item unavailable (@item)', array('@plugin' => $this->pluginInfo['label'], '@item' => $job_item->item_type . ':' . $job_item->item_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUri(TMGMTJobItem $job_item) {
|
||||
return array(
|
||||
'path' => '',
|
||||
'options' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItemTypes() {
|
||||
return isset($this->pluginInfo['item types']) ? $this->pluginInfo['item types'] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItemTypeLabel($type) {
|
||||
$types = $this->getItemTypes();
|
||||
if (isset($types[$type])) {
|
||||
return $types[$type];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item) {
|
||||
return ucfirst($job_item->item_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the abstract translator base plugin class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default controller class for service plugins.
|
||||
*
|
||||
* @ingroup tmgmt_translator
|
||||
*/
|
||||
abstract class TMGMTDefaultTranslatorPluginController extends TMGMTPluginBase implements TMGMTTranslatorPluginControllerInterface {
|
||||
|
||||
protected $supportedRemoteLanguages = array();
|
||||
protected $remoteLanguagesMappings = array();
|
||||
|
||||
/**
|
||||
* Characters that indicate the beginning of an escaped string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $escapeStart = '';
|
||||
|
||||
/**
|
||||
* Characters that indicate the end of an escaped string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $escapeEnd = '';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isAvailable(TMGMTTranslator $translator) {
|
||||
// Assume that the translation service is always available.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canTranslate(TMGMTTranslator $translator, TMGMTJob $job) {
|
||||
// The job is only translatable if the translator is available too.
|
||||
if ($this->isAvailable($translator) && array_key_exists($job->target_language, $translator->getSupportedTargetLanguages($job->source_language))) {
|
||||
// We can only translate this job if the target language of the job is in
|
||||
// one of the supported languages.
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function abortTranslation(TMGMTJob $job) {
|
||||
// Assume that we can abort a translation job at any time.
|
||||
$job->aborted();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultRemoteLanguagesMappings() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedRemoteLanguages(TMGMTTranslator $translator) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRemoteLanguagesMappings(TMGMTTranslator $translator) {
|
||||
if (!empty($this->remoteLanguagesMappings)) {
|
||||
return $this->remoteLanguagesMappings;
|
||||
}
|
||||
|
||||
foreach (language_list() as $language => $info) {
|
||||
$this->remoteLanguagesMappings[$language] = $this->mapToRemoteLanguage($translator, $language);
|
||||
}
|
||||
|
||||
return $this->remoteLanguagesMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapToRemoteLanguage(TMGMTTranslator $translator, $language) {
|
||||
if (!tmgmt_provide_remote_languages_mappings($translator)) {
|
||||
return $language;
|
||||
}
|
||||
|
||||
if (!empty($translator->settings['remote_languages_mappings'][$language])) {
|
||||
return $translator->settings['remote_languages_mappings'][$language];
|
||||
}
|
||||
|
||||
$default_mappings = $this->getDefaultRemoteLanguagesMappings();
|
||||
|
||||
if (isset($default_mappings[$language])) {
|
||||
return $default_mappings[$language];
|
||||
}
|
||||
|
||||
return $language;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mapToLocalLanguage(TMGMTTranslator $translator, $language) {
|
||||
if (!tmgmt_provide_remote_languages_mappings($translator)) {
|
||||
return $language;
|
||||
}
|
||||
|
||||
if (isset($translator->settings['remote_languages_mappings']) && is_array($translator->settings['remote_languages_mappings'])) {
|
||||
$mappings = $translator->settings['remote_languages_mappings'];
|
||||
}
|
||||
else {
|
||||
$mappings = $this->getDefaultRemoteLanguagesMappings();
|
||||
}
|
||||
|
||||
if ($remote_language = array_search($language, $mappings)) {
|
||||
return $remote_language;
|
||||
}
|
||||
|
||||
return $language;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedTargetLanguages(TMGMTTranslator $translator, $source_language) {
|
||||
$languages = entity_metadata_language_list();
|
||||
unset($languages[LANGUAGE_NONE], $languages[$source_language]);
|
||||
return drupal_map_assoc(array_keys($languages));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Default implementation that gets target languages for each remote language.
|
||||
* This approach is ineffective and therefore it is advised that a plugin
|
||||
* should provide own implementation.
|
||||
*/
|
||||
public function getSupportedLanguagePairs(TMGMTTranslator $translator) {
|
||||
$language_pairs = array();
|
||||
|
||||
foreach ($this->getSupportedRemoteLanguages($translator) as $source_language) {
|
||||
foreach ($this->getSupportedTargetLanguages($translator, $source_language) as $target_language) {
|
||||
$language_pairs[] = array('source_language' => $source_language, 'target_language' => $target_language);
|
||||
}
|
||||
}
|
||||
|
||||
return $language_pairs;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNotCanTranslateReason(TMGMTJob $job) {
|
||||
$wrapper = entity_metadata_wrapper('tmgmt_job', $job);
|
||||
return t('@translator can not translate from @source to @target.', array('@translator' => $job->getTranslator()->label(), '@source' => $wrapper->source_language->label(), '@target' => $wrapper->target_language->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNotAvailableReason(TMGMTTranslator $translator) {
|
||||
return t('@translator is not available. Make sure it is properly !configured.', array('@translator' => $this->pluginInfo['label'], '!configured' => l(t('configured'), 'admin/config/regional/tmgmt_translator/manage/' . $translator->name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultSettings() {
|
||||
$defaults = array('auto_accept' => FALSE);
|
||||
// Check if any default settings are defined in the plugin info.
|
||||
if (isset($this->pluginInfo['default settings'])) {
|
||||
return array_merge($defaults, $this->pluginInfo['default settings']);
|
||||
}
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCheckoutSettings(TMGMTJob $job) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function acceptetDataItem(TMGMTJobItem $job_item, array $key) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function escapeText(array $data_item) {
|
||||
if (empty($data_item['#escape'])) {
|
||||
return $data_item['#text'];
|
||||
}
|
||||
|
||||
$text = $data_item['#text'];
|
||||
$escape = $data_item['#escape'];
|
||||
|
||||
// Sort them in reverse order based/ on the position and process them,
|
||||
// so that positions don't change.
|
||||
krsort($escape, SORT_NUMERIC);
|
||||
|
||||
foreach ($escape as $position => $info) {
|
||||
$text = substr_replace($text, $this->getEscapedString($info['string']), $position, strlen($info['string']));
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the escaped string.
|
||||
*
|
||||
* Defaults to use the escapeStart and escapeEnd properties but can be
|
||||
* overriden if a non-static replacement pattern is used.
|
||||
*
|
||||
* @param string $string
|
||||
* String that should be escaped.
|
||||
* @return string
|
||||
*/
|
||||
protected function getEscapedString($string) {
|
||||
return $this->escapeStart . $string . $this->escapeEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unescapeText($text) {
|
||||
return preg_replace('/' . preg_quote($this->escapeStart, '/') . '(.+)' . preg_quote($this->escapeEnd, '/') . '/U', '$1', $text);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* Interface for source ui controllers.
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
interface TMGMTSourceUIControllerInterface extends TMGMTPluginBaseInterface {
|
||||
|
||||
/**
|
||||
* Form callback for the job item review form.
|
||||
*/
|
||||
public function reviewForm($form, &$form_state, TMGMTJobItem $item);
|
||||
|
||||
/**
|
||||
* Form callback for the data item element form.
|
||||
*/
|
||||
public function reviewDataItemElement($form, &$form_state, $data_item_key, $parent_key, array $data_item, TMGMTJobItem $item);
|
||||
|
||||
/**
|
||||
* Validation callback for the job item review form.
|
||||
*/
|
||||
public function reviewFormValidate($form, &$form_state, TMGMTJobItem $item);
|
||||
|
||||
/**
|
||||
* Submit callback for the job item review form.
|
||||
*/
|
||||
public function reviewFormSubmit($form, &$form_state, TMGMTJobItem $item);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see tmgmt_ui_menu().
|
||||
*/
|
||||
public function hook_menu();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see tmgmt_ui_forms().
|
||||
*/
|
||||
public function hook_forms();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see tmgmt_ui_views_default_views().
|
||||
*/
|
||||
public function hook_views_default_views();
|
||||
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Interface for translator ui controllers.
|
||||
*
|
||||
* @ingroup tmgmt_translator
|
||||
*/
|
||||
interface TMGMTTranslatorUIControllerInterface extends TMGMTPluginBaseInterface {
|
||||
|
||||
/**
|
||||
* Form callback for the plugin settings form.
|
||||
*/
|
||||
public function pluginSettingsForm($form, &$form_state, TMGMTTranslator $translator, $busy = FALSE);
|
||||
|
||||
/**
|
||||
* Form callback for the checkout settings form.
|
||||
*/
|
||||
public function checkoutSettingsForm($form, &$form_state, TMGMTJob $job);
|
||||
|
||||
/**
|
||||
* Retrieves information about a translation job.
|
||||
*
|
||||
* Services based translators with remote states should place a Poll button
|
||||
* here to sync the job state.
|
||||
*
|
||||
* @param TMGMTJob $job
|
||||
* The translation job.
|
||||
*/
|
||||
public function checkoutInfo(TMGMTJob $job);
|
||||
|
||||
/**
|
||||
* Form callback for the job item review form.
|
||||
*/
|
||||
public function reviewForm($form, &$form_state, TMGMTJobItem $item);
|
||||
|
||||
/**
|
||||
* Form callback for the data item element form.
|
||||
*/
|
||||
public function reviewDataItemElement($form, &$form_state, $data_item_key, $parent_key, array $data_item, TMGMTJobItem $item);
|
||||
|
||||
/**
|
||||
* Validation callback for the job item review form.
|
||||
*/
|
||||
public function reviewFormValidate($form, &$form_state, TMGMTJobItem $item);
|
||||
|
||||
/**
|
||||
* Submit callback for the job item review form.
|
||||
*/
|
||||
public function reviewFormSubmit($form, &$form_state, TMGMTJobItem $item);
|
||||
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* Default ui controller class for source plugin.
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
class TMGMTDefaultSourceUIController extends TMGMTPluginBase implements TMGMTSourceUIControllerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reviewForm($form, &$form_state, TMGMTJobItem $item) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reviewDataItemElement($form, &$form_state, $data_item_key, $parent_key, array $data_item, TMGMTJobItem $item) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reviewFormValidate($form, &$form_state, TMGMTJobItem $item) {
|
||||
// Nothing to do here by default.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reviewFormSubmit($form, &$form_state, TMGMTJobItem $item) {
|
||||
// Nothing to do here by default.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewForm($form, &$form_state, $type) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewFormValidate($form, &$form_state, $type) {
|
||||
// Nothing to do here by default.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewFormSubmit($form, &$form_state, $type) {
|
||||
// Nothing to do here by default.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_menu() {
|
||||
$items = array();
|
||||
if ($types = tmgmt_source_translatable_item_types($this->pluginType)) {
|
||||
$defaults = array(
|
||||
'page callback' => 'drupal_get_form',
|
||||
'access callback' => 'tmgmt_job_access',
|
||||
'access arguments' => array('create'),
|
||||
);
|
||||
if (isset($this->pluginInfo['file'])) {
|
||||
$defaults['file'] = $this->pluginInfo['file'];
|
||||
}
|
||||
if (isset($this->pluginInfo['file path'])) {
|
||||
$defaults['file path'] = $this->pluginInfo['file path'];
|
||||
}
|
||||
foreach ($types as $type => $name) {
|
||||
$items['admin/tmgmt/sources/' . $this->pluginType . '_' . $type] = $defaults + array(
|
||||
'title' => check_plain($name),
|
||||
'page arguments' => array('tmgmt_ui_' . $this->pluginType . '_source_' . $type . '_overview_form', $this->pluginType, $type),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_forms() {
|
||||
$info = array();
|
||||
if ($types = tmgmt_source_translatable_item_types($this->pluginType)) {
|
||||
foreach (array_keys($types) as $type) {
|
||||
$info['tmgmt_ui_' . $this->pluginType . '_source_' . $type . '_overview_form'] = array(
|
||||
'callback' => 'tmgmt_ui_source_overview_form',
|
||||
'wrapper_callback' => 'tmgmt_ui_source_overview_form_defaults',
|
||||
);
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_views_default_views() {
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Default ui controller class for translator plugins.
|
||||
*
|
||||
* @ingroup tmgmt_translator
|
||||
*/
|
||||
class TMGMTDefaultTranslatorUIController extends TMGMTPluginBase implements TMGMTTranslatorUIControllerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function pluginSettingsForm($form, &$form_state, TMGMTTranslator $translator, $busy = FALSE) {
|
||||
|
||||
if (!empty($translator->plugin)) {
|
||||
$controller = tmgmt_translator_plugin_controller($translator->plugin);
|
||||
}
|
||||
|
||||
// If current translator is configured to provide remote language mapping
|
||||
// provide the form to configure mappings, unless it does not exists yet.
|
||||
if (!empty($controller) && tmgmt_provide_remote_languages_mappings($translator)) {
|
||||
|
||||
$form['remote_languages_mappings'] = array(
|
||||
'#tree' => TRUE,
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Remote languages mappings'),
|
||||
'#description' => t('Here you can specify mappings of your local language codes to the translator language codes.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
|
||||
$options = array();
|
||||
foreach ($controller->getSupportedRemoteLanguages($translator) as $language) {
|
||||
$options[$language] = $language;
|
||||
}
|
||||
|
||||
ksort($options);
|
||||
|
||||
foreach ($controller->getRemoteLanguagesMappings($translator) as $local_language => $remote_language) {
|
||||
$form['remote_languages_mappings'][$local_language] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => tmgmt_language_label($local_language) . ' (' . $local_language . ')',
|
||||
'#default_value' => $remote_language,
|
||||
'#size' => 6,
|
||||
);
|
||||
|
||||
if (!empty($options)) {
|
||||
$form['remote_languages_mappings'][$local_language]['#type'] = 'select';
|
||||
$form['remote_languages_mappings'][$local_language]['#options'] = $options;
|
||||
$form['remote_languages_mappings'][$local_language]['#empty_option'] = ' - ';
|
||||
unset($form['remote_languages_mappings'][$local_language]['#size']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!element_children($form)) {
|
||||
$form['#description'] = t("The @plugin plugin doesn't provide any settings.", array('@plugin' => $this->pluginInfo['label']));
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkoutSettingsForm($form, &$form_state, TMGMTJob $job) {
|
||||
if (!element_children($form)) {
|
||||
$form['#description'] = t("The @translator translator doesn't provide any checkout settings.", array('@translator' => $job->getTranslator()->label()));
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkoutInfo(TMGMTJob $job) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a simple wrapper for the checkout info fieldset.
|
||||
*
|
||||
* @param TMGMTJob $job
|
||||
* Translation job object.
|
||||
* @param $form
|
||||
* Partial form structure to be wrapped in the fieldset.
|
||||
*
|
||||
* @return
|
||||
* The provided form structure wrapped in a collapsed fieldset.
|
||||
*/
|
||||
public function checkoutInfoWrapper(TMGMTJob $job, $form) {
|
||||
$label = $job->getTranslator()->label();
|
||||
$form += array(
|
||||
'#title' => t('@translator translation job information', array('@translator' => $label)),
|
||||
'#type' => 'fieldset',
|
||||
'#collapsible' => TRUE,
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reviewForm($form, &$form_state, TMGMTJobItem $item) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reviewDataItemElement($form, &$form_state, $data_item_key, $parent_key, array $data_item, TMGMTJobItem $item) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reviewFormValidate($form, &$form_state, TMGMTJobItem $item) {
|
||||
// Nothing to do here by default.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reviewFormSubmit($form, &$form_state, TMGMTJobItem $item) {
|
||||
// Nothing to do here by default.
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
.tmgmt-entity-sources-wrapper .form-item {
|
||||
float: left;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.tmgmt-entity-sources-wrapper #edit-search-submit {
|
||||
margin: 26px 10px 0 10px;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
name = "Entity source plugin tests"
|
||||
description = "Support module for entity source testing."
|
||||
package = Testing
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Tests module for the Entity Source plugin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info_alter().
|
||||
*/
|
||||
function tmgmt_entity_test_entity_info_alter(&$entity_info) {
|
||||
if (isset($entity_info['taxonomy_term']) && empty($entity_info['taxonomy_term']['translation'])) {
|
||||
$entity_info['taxonomy_term']['translation'] = array(
|
||||
'tmgmt_entity_test_translation' => array(
|
||||
'base path' => 'taxonomy/term/%taxonomy_term',
|
||||
'alias' => TRUE,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Entity Translation Management module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup tmgmt_source
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows to alter $query used to list entities on specific entity type overview
|
||||
* pages.
|
||||
*
|
||||
* @see TMGMTEntityDefaultSourceUIController
|
||||
*/
|
||||
function hook_tmgmt_entity_type_list_query_alter(EntityFieldQuery $query) {
|
||||
$query->entityCondition('type', array('article', 'page'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup tmgmt_source".
|
||||
*/
|
@@ -0,0 +1,27 @@
|
||||
name = Entity Source
|
||||
description = Entity source plugin for the Translation Management system.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = tmgmt
|
||||
dependencies[] = tmgmt_field
|
||||
dependencies[] = entity
|
||||
dependencies[] = entity_translation
|
||||
|
||||
test_dependencies[] = pathauto
|
||||
test_dependencies[] = file_entity
|
||||
test_dependencies[] = entityreference
|
||||
|
||||
files[] = tmgmt_entity.source.test
|
||||
files[] = tmgmt_entity.source.none.test
|
||||
files[] = tmgmt_entity.pathauto.test
|
||||
files[] = tmgmt_entity.suggestions.test
|
||||
files[] = tmgmt_entity.plugin.inc
|
||||
files[] = tmgmt_entity.ui.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Source plugin for the Translation Management system that handles entities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_plugin_info().
|
||||
*/
|
||||
function tmgmt_entity_tmgmt_source_plugin_info() {
|
||||
$info['entity'] = array(
|
||||
'label' => t('Entity'),
|
||||
'description' => t('Source handler for entities.'),
|
||||
'plugin controller class' => 'TMGMTEntitySourcePluginController',
|
||||
'item types' => array(),
|
||||
);
|
||||
|
||||
$entity_types = array_filter(variable_get('entity_translation_entity_types', array()));
|
||||
|
||||
foreach ($entity_types as $entity_key) {
|
||||
$entity_info = entity_get_info($entity_key);
|
||||
$info['entity']['item types'][$entity_key] = $entity_info['label'];
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_ID_alter().
|
||||
*
|
||||
* Alters comment node type select box to filter out comment types that belongs
|
||||
* to non entity translatable node types.
|
||||
*/
|
||||
function tmgmt_entity_form_tmgmt_ui_entity_source_comment_overview_form_alter(&$form, &$form_state) {
|
||||
|
||||
if (!isset($form['search_wrapper']['search']['node_type'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Change the select name to "type" as in the query submitted value will be
|
||||
// passed into node.type condition.
|
||||
$form['search_wrapper']['search']['type'] = $form['search_wrapper']['search']['node_type'];
|
||||
unset($form['search_wrapper']['search']['node_type']);
|
||||
|
||||
// Set new default value.
|
||||
$form['search_wrapper']['search']['type']['#default_value'] = isset($_GET['type']) ? $_GET['type'] : NULL;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get entity translatable bundles.
|
||||
*
|
||||
* Note that for comment entity type it will return the same as for node as
|
||||
* comment bundles have no use (i.e. in queries).
|
||||
*
|
||||
* @param string $entity_type
|
||||
* Drupal entity type.
|
||||
*
|
||||
* @return array
|
||||
* Array of key => values, where key is type and value its label.
|
||||
*/
|
||||
function tmgmt_entity_get_translatable_bundles($entity_type) {
|
||||
|
||||
// If given entity type does not have entity translations enabled, no reason
|
||||
// to continue.
|
||||
if (!in_array($entity_type, variable_get('entity_translation_entity_types', array()))) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
$translatable_bundle_types = array();
|
||||
|
||||
foreach ($entity_info['bundles'] as $bundle_type => $bundle_definition) {
|
||||
|
||||
if ($entity_type == 'comment') {
|
||||
$bundle_type = str_replace('comment_node_', '', $bundle_type);
|
||||
if (variable_get('language_content_type_' . $bundle_type) == ENTITY_TRANSLATION_ENABLED) {
|
||||
$translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
|
||||
}
|
||||
}
|
||||
elseif ($entity_type == 'node') {
|
||||
if (variable_get('language_content_type_' . $bundle_type) == ENTITY_TRANSLATION_ENABLED) {
|
||||
$translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
|
||||
}
|
||||
}
|
||||
|
||||
return $translatable_bundle_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets translatable entities of a given type.
|
||||
*
|
||||
* Additionally you can specify entity property conditions, pager and limit.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* Drupal entity type.
|
||||
* @param array $property_conditions
|
||||
* Entity properties. There is no value processing so caller must make sure
|
||||
* the provided entity property exists for given entity type and its value
|
||||
* is processed.
|
||||
* @param bool $pager
|
||||
* Flag to determine if pager will be used.
|
||||
*
|
||||
* @return array
|
||||
* Array of translatable entities.
|
||||
*/
|
||||
function tmgmt_entity_get_translatable_entities($entity_type, $property_conditions = array(), $pager = FALSE) {
|
||||
|
||||
if (!in_array($entity_type, variable_get('entity_translation_entity_types', array()))) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$languages = drupal_map_assoc(array_keys(language_list()));
|
||||
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
$label_key = isset($entity_info['entity keys']['label']) ? $entity_info['entity keys']['label'] : NULL;
|
||||
|
||||
$id_key = $entity_info['entity keys']['id'];
|
||||
$query = db_select($entity_info['base table'], 'e');
|
||||
$query->addTag('tmgmt_entity_get_translatable_entities');
|
||||
$query->addField('e', $id_key);
|
||||
|
||||
// Language neutral entities are not translatable. Filter them out. To do
|
||||
// that: join {entity_translation} table, but only records with source column
|
||||
// empty. The {entity_translation}.language will represent the original entity
|
||||
// language in that case.
|
||||
$source_table_alias = $query->leftJoin('entity_translation', NULL, "%alias.entity_type = :entity_type AND %alias.entity_id = e.$id_key AND %alias.source = ''", array(':entity_type' => $entity_type));
|
||||
$query->condition("$source_table_alias.language", LANGUAGE_NONE, '<>');
|
||||
|
||||
// Searching for sources with missing translation.
|
||||
if (!empty($property_conditions['target_status']) && !empty($property_conditions['target_language']) && in_array($property_conditions['target_language'], $languages)) {
|
||||
|
||||
$translation_table_alias = db_escape_field('et_' . $property_conditions['target_language']);
|
||||
$query->leftJoin('entity_translation', $translation_table_alias, "%alias.entity_type = :entity_type AND %alias.entity_id = e.$id_key AND %alias.language = :language",
|
||||
array(':entity_type' => $entity_type, ':language' => $property_conditions['target_language']));
|
||||
|
||||
// Exclude entities with having source language same as the target language
|
||||
// we search for.
|
||||
$query->condition('e.language', $property_conditions['target_language'], '<>');
|
||||
|
||||
if ($property_conditions['target_status'] == 'untranslated_or_outdated') {
|
||||
$or = db_or();
|
||||
$or->isNull("$translation_table_alias.language");
|
||||
$or->condition("$translation_table_alias.translate", 1);
|
||||
$query->condition($or);
|
||||
}
|
||||
elseif ($property_conditions['target_status'] == 'outdated') {
|
||||
$query->condition("$translation_table_alias.translate", 1);
|
||||
}
|
||||
elseif ($property_conditions['target_status'] == 'untranslated') {
|
||||
$query->isNull("$translation_table_alias.language");
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the condition so we do not try to add it again below.
|
||||
unset($property_conditions['target_language']);
|
||||
unset($property_conditions['target_status']);
|
||||
|
||||
// Searching for the source label.
|
||||
if (!empty($label_key) && isset($property_conditions[$label_key])) {
|
||||
$search_tokens = explode(' ', $property_conditions[$label_key]);
|
||||
$or = db_or();
|
||||
|
||||
foreach ($search_tokens as $search_token) {
|
||||
$search_token = trim($search_token);
|
||||
if (strlen($search_token) > 2) {
|
||||
$or->condition($label_key, "%$search_token%", 'LIKE');
|
||||
}
|
||||
}
|
||||
|
||||
if ($or->count() > 0) {
|
||||
$query->condition($or);
|
||||
}
|
||||
|
||||
unset($property_conditions[$label_key]);
|
||||
}
|
||||
|
||||
// Searching by taxonomy bundles - we need to switch to vid as the bundle key.
|
||||
if ($entity_type == 'taxonomy_term' && !empty($property_conditions['vocabulary_machine_name'])) {
|
||||
$property_name = 'vid';
|
||||
$vocabulary = taxonomy_vocabulary_machine_name_load($property_conditions['vocabulary_machine_name']);
|
||||
$property_value = $vocabulary->vid;
|
||||
$query->condition('e.' . $property_name, $property_value);
|
||||
// Remove the condition so we do not try to add it again below.
|
||||
unset($property_conditions['vocabulary_machine_name']);
|
||||
}
|
||||
// Searching by the node bundles - that applies for node entities as well as
|
||||
// comment.
|
||||
elseif (in_array($entity_type, array('comment', 'node'))) {
|
||||
$node_table_alias = 'e';
|
||||
|
||||
// For comments join node table so that we can filter based on type.
|
||||
if ($entity_type == 'comment') {
|
||||
$query->join('node', 'n', 'e.nid = n.nid');
|
||||
$node_table_alias = 'n';
|
||||
}
|
||||
|
||||
// Get translatable node types and check if it is worth to continue.
|
||||
$translatable_node_types = array_keys(tmgmt_entity_get_translatable_bundles('node'));
|
||||
if (empty($translatable_node_types)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// If we have type property add condition.
|
||||
if (isset($property_conditions['type'])) {
|
||||
$query->condition($node_table_alias . '.type', $property_conditions['type']);
|
||||
// Remove the condition so we do not try to add it again below.
|
||||
unset($property_conditions['type']);
|
||||
}
|
||||
// If not, query db only for translatable node types.
|
||||
else {
|
||||
$query->condition($node_table_alias . '.type', $translatable_node_types);
|
||||
}
|
||||
}
|
||||
|
||||
// Add remaining query conditions which are expected to be handled in a
|
||||
// generic way.
|
||||
foreach ($property_conditions as $property_name => $property_value) {
|
||||
$query->condition('e.' . $property_name, $property_value);
|
||||
}
|
||||
|
||||
if ($pager) {
|
||||
$query = $query->extend('PagerDefault')->limit(variable_get('tmgmt_source_list_limit', 20));
|
||||
}
|
||||
else {
|
||||
$query->range(0, variable_get('tmgmt_source_list_limit', 20));
|
||||
}
|
||||
|
||||
$query->orderBy($entity_info['entity keys']['id'], 'DESC');
|
||||
$entity_ids = $query->execute()->fetchCol();
|
||||
$entities = array();
|
||||
|
||||
if (!empty($entity_ids)) {
|
||||
$entities = entity_load($entity_type, $entity_ids);
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_suggestions()
|
||||
*/
|
||||
function tmgmt_entity_tmgmt_source_suggestions(array $items, TMGMTJob $job) {
|
||||
$suggestions = array();
|
||||
// Get all translatable entity types.
|
||||
$entity_types = array_filter(variable_get('entity_translation_entity_types', array()));
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (($item instanceof TMGMTJobItem) && ($item->plugin == 'entity') || ($item->plugin == 'node')) {
|
||||
// Load the entity and extract the bundle name to get all fields from the
|
||||
// current entity.
|
||||
$entity = entity_load_single($item->item_type, $item->item_id);
|
||||
list(, , $bundle) = entity_extract_ids($item->item_type, $entity);
|
||||
$field_instances = field_info_instances($item->item_type, $bundle);
|
||||
|
||||
|
||||
// Loop over all fields, check if they are NOT translatable. Only if a
|
||||
// field is not translatable we may suggest a referenced entity. If so,
|
||||
// check for a supported field type (image and file currently here).
|
||||
foreach ($field_instances as $instance) {
|
||||
$field = field_info_field($instance['field_name']);
|
||||
$field_type = $field['type'];
|
||||
$field_name = $field['field_name'];
|
||||
switch ($field_type) {
|
||||
case 'file':
|
||||
case 'image':
|
||||
// 'File' (and images) must be translatable entity types.
|
||||
// Other files we not suggest here. Get all field items from the
|
||||
// current field and suggest them as translatable.
|
||||
if (isset($entity_types['file']) && ($field_items = field_get_items($item->item_type, $entity, $field_name))) {
|
||||
// Add all files as a suggestion.
|
||||
foreach ($field_items as $field_item) {
|
||||
$file_entity = entity_load_single('file', $field_item['fid']);
|
||||
|
||||
// Check if there is already a translation available for this
|
||||
// file. If so, just continue with the next file.
|
||||
$handler = entity_translation_get_handler('file', $file_entity);
|
||||
if ($handler instanceof EntityTranslationHandlerInterface) {
|
||||
$translations = $handler->getTranslations();
|
||||
if (isset($translations->data[$job->target_language])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the translation as a suggestion.
|
||||
$suggestions[] = array(
|
||||
'job_item' => tmgmt_job_item_create('entity', 'file', $file_entity->fid),
|
||||
'reason' => t('Field @label', array('@label' => $instance['label'])),
|
||||
'from_item' => $item->tjiid,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'entityreference':
|
||||
$target_type = $field['settings']['target_type'];
|
||||
// Make sure only tranlatable entity types are suggested.
|
||||
if (isset($entity_types[$target_type]) && ($field_items = field_get_items($item->item_type, $entity, $field_name))) {
|
||||
// Add all referenced entities as suggestion.
|
||||
foreach ($field_items as $field_item) {
|
||||
$ref_entity = entity_load_single($target_type, $field_item['target_id']);
|
||||
|
||||
// Check if there is already a translation available for this
|
||||
// entity. If so, just continue with the next one.
|
||||
$handler = entity_translation_get_handler($target_type, $ref_entity);
|
||||
if ($handler instanceof EntityTranslationHandlerInterface) {
|
||||
$translations = $handler->getTranslations();
|
||||
if (isset($translations->data[$job->target_language])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add suggestion.
|
||||
$suggestions[] = array(
|
||||
'job_item' => tmgmt_job_item_create('entity', $target_type, $field_item['target_id']),
|
||||
'reason' => t('Field @label', array('@label' => $instance['label'])),
|
||||
'from_item' => $item->tjiid,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $suggestions;
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Tests integration with pathauto.
|
||||
*/
|
||||
class TMGMTEntitySourcePathAutoTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source Pathauto tests',
|
||||
'description' => 'Verifies that the correct aliases are generated for entity transations',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('entity_translation', 'pathauto'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity', 'entity_translation', 'pathauto'));
|
||||
$this->loginAsAdmin();
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that pathauto aliases are correctly created.
|
||||
*/
|
||||
function testAliasCreation() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
// Create a node.
|
||||
$node = $this->createNode('article');
|
||||
// Create a job item for this node and add it to the job.
|
||||
$job->addItem('entity', 'node', $node->nid);
|
||||
|
||||
// Translate the job.
|
||||
$job->requestTranslation();
|
||||
|
||||
// Check the translated job items.
|
||||
foreach ($job->getItems() as $item) {
|
||||
$item->acceptTranslation();
|
||||
}
|
||||
|
||||
// Make sure that the correct url aliases were created.
|
||||
$aliases = db_query('SELECT * FROM {url_alias} where source = :source', array(':source' => 'node/' . $node->nid))->fetchAllAssoc('language');
|
||||
$this->assertEqual(2, count($aliases));
|
||||
$this->assertTrue(isset($aliases['en']), 'English alias created.');
|
||||
$this->assertTrue(isset($aliases['de']), 'German alias created.');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the Entity source controller.
|
||||
*/
|
||||
|
||||
class TMGMTEntitySourcePluginController extends TMGMTDefaultSourcePluginController {
|
||||
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
if ($entity = entity_load_single($job_item->item_type, $job_item->item_id)) {
|
||||
return entity_label($job_item->item_type, $entity);
|
||||
}
|
||||
}
|
||||
|
||||
public function getUri(TMGMTJobItem $job_item) {
|
||||
if ($entity = entity_load_single($job_item->item_type, $job_item->item_id)) {
|
||||
return entity_uri($job_item->item_type, $entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Returns the data from the fields as a structure that can be processed by
|
||||
* the Translation Management system.
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
$entity = entity_load_single($job_item->item_type, $job_item->item_id);
|
||||
if (!$entity) {
|
||||
throw new TMGMTException(t('Unable to load entity %type with id %id', array('%type' => $job_item->item_type, $job_item->item_id)));
|
||||
}
|
||||
if (entity_language($job_item->item_type, $entity) == LANGUAGE_NONE) {
|
||||
throw new TMGMTException(t('Entity %entity could not be translated because it is language neutral', array('%entity' => entity_label($job_item->item_type, $entity))));
|
||||
}
|
||||
return tmgmt_field_get_source_data($job_item->item_type, $entity, $job_item->getJob()->source_language, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item) {
|
||||
$entity = entity_load_single($job_item->item_type, $job_item->item_id);
|
||||
$job = tmgmt_job_load($job_item->tjid);
|
||||
|
||||
tmgmt_field_populate_entity($job_item->item_type, $entity, $job->target_language, $job_item->getData());
|
||||
|
||||
// Change the active language of the entity to the target language.
|
||||
$handler = entity_translation_get_handler($job_item->item_type, $entity);
|
||||
$handler->setFormLanguage($job_item->getJob()->target_language);
|
||||
|
||||
entity_save($job_item->item_type, $entity);
|
||||
|
||||
$translation = array(
|
||||
// @todo Improve hardcoded values.
|
||||
'translate' => 0,
|
||||
'status' => TRUE,
|
||||
'language' => $job_item->getJob()->target_language,
|
||||
'source' => $job_item->getJob()->source_language,
|
||||
);
|
||||
$handler->setTranslation($translation);
|
||||
$handler->saveTranslations();
|
||||
$job_item->accepted();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item) {
|
||||
if ($entity = entity_load_single($job_item->item_type, $job_item->item_id)) {
|
||||
$bundles = tmgmt_entity_get_translatable_bundles($job_item->item_type);
|
||||
$info = entity_get_info($job_item->item_type);
|
||||
list(, , $bundle) = entity_extract_ids($job_item->item_type, $entity);
|
||||
// Display entity type and label if we have one and the bundle isn't
|
||||
// the same as the entity type.
|
||||
if (isset($bundles[$bundle]) && $bundle != $job_item->item_type) {
|
||||
return t('@type (@bundle)', array('@type' => $info['label'], '@bundle' => $bundles[$bundle]));
|
||||
}
|
||||
// Otherwise just display the entity type label.
|
||||
elseif (isset($info['label'])) {
|
||||
return $info['label'];
|
||||
}
|
||||
return parent::getType($job_item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item) {
|
||||
$entity = entity_load_single($job_item->item_type, $job_item->item_id);
|
||||
return isset($entity->translations->original) ? $entity->translations->original : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
if ($entity = entity_load_single($job_item->item_type, $job_item->item_id)) {
|
||||
$entity_info = entity_get_info($job_item->item_type);
|
||||
if (isset($entity_info['entity keys']['translations'])){
|
||||
$translations_key = $entity_info['entity keys']['translations'];
|
||||
return array_keys($entity->{$translations_key}->data);
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Entity source LANGUAGE_NONE tests.
|
||||
*
|
||||
* Splitted into a separate test because of https://www.drupal.org/node/2675230.
|
||||
*/
|
||||
class TMGMTEntitySourceLanguageNoneTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
public $vocabulary;
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source Neutral tests',
|
||||
'description' => 'Tests that LANGUAGE_NONE entities can not be translated',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('entity_translation'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity', 'taxonomy', 'entity_translation'));
|
||||
|
||||
// Admin user to perform settings on setup.
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
|
||||
$this->vocabulary = $this->createTaxonomyVocab(strtolower($this->randomName()), $this->randomName(), array(FALSE, TRUE, TRUE, TRUE));
|
||||
|
||||
// Enable entity translations for taxonomy.
|
||||
$edit['entity_translation_entity_types[taxonomy_term]'] = 1;
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if language neutral entities are not allowed for translation.
|
||||
*
|
||||
* That behaviour is described in the entity_translation documentation:
|
||||
* https://www.drupal.org/node/1280934
|
||||
*/
|
||||
function testLanguageNeutral() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Structure: array({entity-type} => array({source-langcode} => {entity}))
|
||||
$test_data = array();
|
||||
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
$test_data['node'][LANGUAGE_NONE] = $this->createNode('article', LANGUAGE_NONE);
|
||||
$test_data['node']['en'] = $this->createNode('article', 'en');
|
||||
$test_data['node']['de'] = $this->createNode('article', 'de');
|
||||
|
||||
$test_data['taxonomy_term'][LANGUAGE_NONE] = $this->createTaxonomyTerm($this->vocabulary, LANGUAGE_NONE);
|
||||
$test_data['taxonomy_term']['en'] = $this->createTaxonomyTerm($this->vocabulary, 'en');
|
||||
$test_data['taxonomy_term']['de'] = $this->createTaxonomyTerm($this->vocabulary, 'de');
|
||||
|
||||
// Test if tmgmt_entity_get_translatable_entities() function excludes
|
||||
// language neutral entities.
|
||||
foreach ($test_data as $entity_type => $entities) {
|
||||
$translatable_entities = tmgmt_entity_get_translatable_entities($entity_type);
|
||||
foreach ($entities as $langcode => $entity) {
|
||||
list($id, , ) = entity_extract_ids($entity_type, $entity);
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
$this->assert(!isset($translatable_entities[$id]), "Language neutral $entity_type entity does not exist in the translatable entities list.");
|
||||
}
|
||||
else {
|
||||
$this->assert(isset($translatable_entities[$id]), "$langcode $entity_type entity exists in the translatable entities list.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test if language neutral entities can't be added to a translation job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
foreach ($test_data as $entity_type => $entities) {
|
||||
foreach ($entities as $langcode => $entity) {
|
||||
list($id, , ) = entity_extract_ids($entity_type, $entity);
|
||||
try {
|
||||
$job->addItem('entity', $entity_type, $id);
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
$this->fail("Adding of language neutral $entity_type entity to a translation job did not fail.");
|
||||
}
|
||||
else {
|
||||
$this->pass("Adding of $langcode $entity_type entity node to a translation job did not fail.");
|
||||
}
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
$this->pass("Adding of language neutral $entity_type entity to a translation job did fail.");
|
||||
}
|
||||
else {
|
||||
$this->fail("Adding of $langcode $entity_type entity node to a translation job did fail.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$GLOBALS['TMGMT_DEBUG'] = FALSE;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Entity Source tests.
|
||||
*/
|
||||
class TMGMTEntitySourceTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
public $vocabulary;
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source tests',
|
||||
'description' => 'Exporting source data from entities and saving translations back to entities.',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('entity_translation'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity', 'taxonomy', 'entity_translation'));
|
||||
|
||||
// Admin user to perform settings on setup.
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
|
||||
$this->vocabulary = $this->createTaxonomyVocab(strtolower($this->randomName()), $this->randomName(), array(FALSE, TRUE, TRUE, TRUE));
|
||||
|
||||
// Enable entity translations for taxonomy.
|
||||
$edit['entity_translation_entity_types[taxonomy_term]'] = 1;
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests nodes field translation.
|
||||
*/
|
||||
function testEntitySourceNode() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
// Create some nodes.
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$node = $this->createNode('article');
|
||||
// Create a job item for this node and add it to the job.
|
||||
$item = $job->addItem('entity', 'node', $node->nid);
|
||||
$this->assertEqual(t('@type (@bundle)', array('@type' => t('Node'), '@bundle' => 'Article')), $item->getSourceType());
|
||||
}
|
||||
|
||||
// Translate the job.
|
||||
$job->requestTranslation();
|
||||
|
||||
// Check the translated job items.
|
||||
foreach ($job->getItems() as $item) {
|
||||
// The source is available only for en.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
$this->assertTrue($item->isAccepted());
|
||||
$entity = entity_load_single($item->item_type, $item->item_id);
|
||||
$data = $item->getData();
|
||||
$this->checkTranslatedData($entity, $data, 'de');
|
||||
$this->checkUntranslatedData($entity, $this->field_names['node']['article'], $data, 'de');
|
||||
// The source is now available for both en and de.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests taxonomy terms field translation.
|
||||
*/
|
||||
function testEntitySourceTerm() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Create the job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$term = NULL;
|
||||
|
||||
//Create some terms.
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$term = $this->createTaxonomyTerm($this->vocabulary);
|
||||
// Create the item and assign it to the job.
|
||||
$item = $job->addItem('entity', 'taxonomy_term', $term->tid);
|
||||
$this->assertEqual(t('@type (@bundle)', array('@type' => t('Taxonomy term'), '@bundle' => $this->vocabulary->name)), $item->getSourceType());
|
||||
}
|
||||
// Request the translation and accept it.
|
||||
$job->requestTranslation();
|
||||
|
||||
// Check if the fields were translated.
|
||||
foreach ($job->getItems() as $item) {
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
$entity = entity_load_single($item->item_type, $item->item_id);
|
||||
$data = $item->getData();
|
||||
$this->checkTranslatedData($entity, $data, 'de');
|
||||
$this->checkUntranslatedData($entity, $this->field_names['taxonomy_term'][$this->vocabulary->machine_name], $data, 'de');
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
}
|
||||
|
||||
function testAddingJobItemsWithEmptySourceText() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Create term with empty texts.
|
||||
$empty_term = new stdClass();
|
||||
$empty_term->name = $this->randomName();
|
||||
$empty_term->description = $this->randomName();
|
||||
$empty_term->vid = $this->vocabulary->vid;
|
||||
taxonomy_term_save($empty_term);
|
||||
|
||||
// Create the job.
|
||||
$job = tmgmt_job_create('en', NULL);
|
||||
try {
|
||||
$job->addItem('entity', 'taxonomy_term', $empty_term->tid);
|
||||
$this->fail('Job item added with empty source text.');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$this->assert(empty($job->tjid), 'After adding a job item with empty source text its tjid has to be unset.');
|
||||
}
|
||||
|
||||
// Create term with populated source content.
|
||||
$populated_content_term = $this->createTaxonomyTerm($this->vocabulary);
|
||||
|
||||
// Lets reuse the last created term with populated source content.
|
||||
$job->addItem('entity', 'taxonomy_term', $populated_content_term->tid);
|
||||
$this->assert(!empty($job->tjid), 'After adding another job item with populated source text its tjid must be set.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the source is able to pull content in requested language.
|
||||
*/
|
||||
function testRequestDataForSpecificLanguage() {
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('cs');
|
||||
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob('en', 'de');
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$node = $this->createNode('article', 'cs');
|
||||
$node->body['en'][0]['value'] = 'en translation';
|
||||
node_save($node);
|
||||
$job->addItem('entity', 'node', $node->nid);
|
||||
|
||||
$data = $job->getData();
|
||||
$this->assertEqual($data[1]['body'][0]['value']['#text'], 'en translation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the data from an entity with the translated data.
|
||||
*
|
||||
* @param $tentity
|
||||
* The translated entity object.
|
||||
* @param $data
|
||||
* An array with the translated data.
|
||||
* @param $langcode
|
||||
* The code of the target language.
|
||||
*/
|
||||
function checkTranslatedData($tentity, $data, $langcode) {
|
||||
foreach (element_children($data) as $field_name) {
|
||||
foreach (element_children($data[$field_name]) as $delta) {
|
||||
foreach (element_children($data[$field_name][$delta]) as $column) {
|
||||
$column_value = $data[$field_name][$delta][$column];
|
||||
if (!empty($column_value['#translate'])) {
|
||||
$this->assertEqual($tentity->{$field_name}[$langcode][$delta][$column], $column_value['#translation']['#text'], format_string('The field %field:%delta has been populated with the proper translated data.', array('%field' => $field_name, 'delta' => $delta)));
|
||||
}
|
||||
else {
|
||||
$this->assertEqual($tentity->{$field_name}[$langcode][$delta][$column], $column_value['#text'], format_string('The field %field:%delta has been populated with the proper untranslated data.', array('%field' => $field_name, 'delta' => $delta)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the fields that should not be translated.
|
||||
*
|
||||
* @param $tentity
|
||||
* The translated entity object.
|
||||
* @param $fields
|
||||
* An array with the field names to check.
|
||||
* @param $translation
|
||||
* An array with the translated data.
|
||||
* @param $langcode
|
||||
* The code of the target language.
|
||||
*/
|
||||
function checkUntranslatedData($tentity, $fields, $data, $langcode) {
|
||||
foreach ($fields as $field_name) {
|
||||
$field_info = field_info_field($field_name);
|
||||
if (!$field_info['translatable']) {
|
||||
// Avoid some PHP warnings.
|
||||
if (isset($data[$field_name])) {
|
||||
$this->assertNull($data[$field_name]['#translation']['#text'], 'The not translatable field was not translated.');
|
||||
}
|
||||
if (isset($tentity->{$field_name}[$langcode])) {
|
||||
$this->assertNull($tentity->{$field_name}[$langcode], 'The entity has translated data in a field that is translatable.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
/*
|
||||
* @file
|
||||
* Contains tests for Translation management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic Source-Suggestions tests.
|
||||
*/
|
||||
class TMGMTSuggestionsTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Suggestions tests',
|
||||
'description' => 'Tests suggestion implementation for the entity source plugin',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array(
|
||||
'file_entity',
|
||||
'entityreference',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp(array('file_entity', 'tmgmt_entity', 'tmgmt_ui', 'entityreference', 'tmgmt_i18n_string', 'i18n_menu'));
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Enable entity translations for nodes and comments.
|
||||
$edit = array();
|
||||
$edit['entity_translation_entity_types[node]'] = 1;
|
||||
$edit['entity_translation_entity_types[file]'] = 1;
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a node to get suggestions from.
|
||||
*
|
||||
* Creates a node with two file fields. The first one is not translatable,
|
||||
* the second one is. Both fields got two files attached, where one has
|
||||
* translatable content (title and atl-text) and the other one not.
|
||||
*
|
||||
* @return object
|
||||
* The node which is prepared with all needed fields for the suggestions.
|
||||
*/
|
||||
protected function prepareTranslationSuggestions() {
|
||||
// Create a content type with fields.
|
||||
// Only the first field is a translatable reference.
|
||||
$type = $this->drupalCreateContentType();
|
||||
|
||||
$field1 = field_create_field(array(
|
||||
'field_name' => 'field1',
|
||||
'type' => 'file',
|
||||
'cardinality' => -1,
|
||||
));
|
||||
$field2 = field_create_field(array(
|
||||
'field_name' => 'field2',
|
||||
'type' => 'file',
|
||||
'cardinality' => -1,
|
||||
'translatable' => TRUE,
|
||||
));
|
||||
$field3 = field_create_field(array(
|
||||
'field_name' => 'field3',
|
||||
'type' => 'entityreference',
|
||||
'cardinality' => -1,
|
||||
'settings' => array(
|
||||
'target_type' => 'node',
|
||||
'handler' => 'base',
|
||||
'handler_settings' => array(
|
||||
'target_bundles' => array($type->type => $type->type),
|
||||
'sort' => array('type' => 'none'),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Create field instances on the content type.
|
||||
field_create_instance(array(
|
||||
'field_name' => $field1['field_name'],
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->type,
|
||||
'label' => 'Field 1',
|
||||
'widget' => array('type' => 'file'),
|
||||
'settings' => array(),
|
||||
));
|
||||
field_create_instance(array(
|
||||
'field_name' => $field2['field_name'],
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->type,
|
||||
'label' => 'Field 2',
|
||||
'widget' => array('type' => 'file'),
|
||||
'settings' => array(),
|
||||
));
|
||||
field_create_instance(array(
|
||||
'field_name' => $field3['field_name'],
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->type,
|
||||
'label' => 'Field 3',
|
||||
'settings' => array(),
|
||||
'widget' => array('type' => 'entityreference_autocomplete_tags'),
|
||||
));
|
||||
|
||||
// Make the body field translatable from node.
|
||||
$info = field_info_field('body');
|
||||
$info['translatable'] = TRUE;
|
||||
field_update_field($info);
|
||||
|
||||
// Make the file entity fields translatable.
|
||||
$info = field_info_field('field_file_image_alt_text');
|
||||
$info['translatable'] = TRUE;
|
||||
field_update_field($info);
|
||||
|
||||
$info = field_info_field('field_file_image_title_text');
|
||||
$info['translatable'] = TRUE;
|
||||
field_update_field($info);
|
||||
|
||||
// Create and save files - two with some text and two with no text.
|
||||
list($file1, $file2, $file3, $file4) = $this->drupalGetTestFiles('image');
|
||||
$file2->field_file_image_alt_text['en'][0] = array(
|
||||
'value' => $this->randomName(),
|
||||
'type' => 'plain_text',
|
||||
);
|
||||
$file2->field_file_image_title_text['en'][0] = array(
|
||||
'value' => $this->randomName() . ' ' . $this->randomName(),
|
||||
'type' => 'plain_text',
|
||||
);
|
||||
|
||||
$file4->field_file_image_alt_text['en'][0] = array(
|
||||
'value' => $this->randomName(),
|
||||
'type' => 'plain_text',
|
||||
);
|
||||
$file4->field_file_image_title_text['en'][0] = array(
|
||||
'value' => $this->randomName() . ' ' . $this->randomName(),
|
||||
'type' => 'plain_text',
|
||||
);
|
||||
|
||||
file_save($file1);
|
||||
file_save($file2);
|
||||
file_save($file3);
|
||||
file_save($file4);
|
||||
|
||||
// Create a dummy node that will be referenced
|
||||
$referenced_node = $this->drupalCreateNode(array(
|
||||
'type' => $type->type,
|
||||
'language' => 'en',
|
||||
'body' => array(
|
||||
'en' => array(
|
||||
array('value' => $this->randomName() . ' ' . $this->randomName()),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Create a node with two translatable and two non-translatable files.
|
||||
$node = $this->drupalCreateNode(array(
|
||||
'type' => $type->type,
|
||||
'language' => 'en',
|
||||
'body' => array('en' => array(
|
||||
array(
|
||||
'value' => $this->randomName(),
|
||||
),
|
||||
)),
|
||||
$field1['field_name'] => array(LANGUAGE_NONE => array(
|
||||
array(
|
||||
'fid' => $file1->fid,
|
||||
'display' => 1,
|
||||
'description' => '',
|
||||
),
|
||||
array(
|
||||
'fid' => $file2->fid,
|
||||
'display' => 1,
|
||||
'description' => '',
|
||||
),
|
||||
)),
|
||||
$field2['field_name'] => array(LANGUAGE_NONE => array(
|
||||
array(
|
||||
'fid' => $file3->fid,
|
||||
'display' => 1,
|
||||
'description' => '',
|
||||
),
|
||||
array(
|
||||
'fid' => $file4->fid,
|
||||
'display' => 1,
|
||||
'description' => '',
|
||||
),
|
||||
)),
|
||||
$field3['field_name'] => array(LANGUAGE_NONE => array(
|
||||
array('target_id' => $referenced_node->nid),
|
||||
)),
|
||||
));
|
||||
|
||||
// Create a translatable menu.
|
||||
$config = array(
|
||||
'menu_name' => 'translatable-menu',
|
||||
'title' => 'Translatable menu',
|
||||
'description' => $this->randomName(),
|
||||
'i18n_mode' => I18N_MODE_MULTIPLE,
|
||||
);
|
||||
menu_save($config);
|
||||
$menu = menu_load($config['menu_name']);
|
||||
|
||||
// Create a menu link for the node.
|
||||
$menu_link = array(
|
||||
'link_path' => 'node/' . $node->nid,
|
||||
'link_title' => 'Menu link one',
|
||||
// i18n_menu_link::get_title() uses the title, set that too.
|
||||
'title' => 'Menu link one',
|
||||
'menu_name' => $menu['menu_name'],
|
||||
'customized' => TRUE,
|
||||
);
|
||||
$node->link = menu_link_load(menu_link_save($menu_link));
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test suggested entities from a translation job.
|
||||
*/
|
||||
public function testSuggestions() {
|
||||
// Prepare a job and a node for testing.
|
||||
$job = $this->createJob();
|
||||
$node = $this->prepareTranslationSuggestions();
|
||||
$item = $job->addItem('entity', 'node', $node->nid);
|
||||
|
||||
// Get all suggestions and clean the list.
|
||||
$suggestions = $job->getSuggestions();
|
||||
$job->cleanSuggestionsList($suggestions);
|
||||
|
||||
// Check for suggestions.
|
||||
$this->assertEqual(count($suggestions), 4, 'Found four suggestions.');
|
||||
|
||||
// Check for valid attributes on the suggestions.
|
||||
foreach ($suggestions as $suggestion) {
|
||||
switch ($suggestion['reason']) {
|
||||
case 'Field Field 1':
|
||||
$this->assertEqual($suggestion['job_item']->getWordCount(), 3, 'Three translatable words in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->plugin, 'entity', 'Got an entity as plugin in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_type, 'file', 'Got a file in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_id, $node->field1[LANGUAGE_NONE][1]['fid'], 'File id match between node and suggestion.');
|
||||
break;
|
||||
case 'Field Field 2':
|
||||
$this->assertEqual($suggestion['job_item']->getWordCount(), 3, 'Three translatable words in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->plugin, 'entity', 'Got an entity as plugin in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_type, 'file', 'Got a file in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_id, $node->field2[LANGUAGE_NONE][1]['fid'], 'File id match between node and suggestion.');
|
||||
break;
|
||||
case 'Field Field 3':
|
||||
$this->assertEqual($suggestion['job_item']->getWordCount(), 2, 'Two translatable words in the suggestion');
|
||||
$this->assertEqual($suggestion['job_item']->plugin, 'entity', 'Got an entity as plugin in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_type, 'node', 'Got a node in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_id, $node->field3[LANGUAGE_NONE][0]['target_id'], 'File id match between node and suggestion.');
|
||||
break;
|
||||
case 'Menu link Menu link one':
|
||||
$this->assertEqual($suggestion['job_item']->getWordCount(), 3, 'Three translatable words in the suggestion' . $suggestion['job_item']->plugin . $suggestion['job_item']->item_type);
|
||||
$this->assertEqual($suggestion['job_item']->plugin, 'i18n_string', 'Got a string as plugin in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_type, 'menu_link', 'Got a menu link in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_id, 'menu:item:'. $node->link['mlid'], 'Menu link id match between menu link and suggestion.');
|
||||
break;
|
||||
default:
|
||||
$this->fail('Found an invalid suggestion.');
|
||||
break;
|
||||
}
|
||||
$this->assertEqual($suggestion['from_item'], $item->tjiid);
|
||||
$job->addExistingItem($suggestion['job_item']);
|
||||
}
|
||||
|
||||
// Re-get all suggestions.
|
||||
$suggestions = $job->getSuggestions();
|
||||
$job->cleanSuggestionsList($suggestions);
|
||||
|
||||
// Check for no more suggestions.
|
||||
$this->assertEqual(count($suggestions), 0, 'Found no more suggestion.');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Abstract entity ui controller class for source plugin that provides
|
||||
* getEntity() method to retrieve list of entities of specific type. It also
|
||||
* allows to implement alter hook to alter the entity query for a specific type.
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
abstract class TMGMTEntityDefaultSourceUIController extends TMGMTDefaultSourceUIController {
|
||||
|
||||
/**
|
||||
* Entity source list items limit.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $pagerLimit = 25;
|
||||
|
||||
/**
|
||||
* Gets entities data of provided type needed to build overview form list.
|
||||
*
|
||||
* @param $type
|
||||
* Entity type for which to get list of entities.
|
||||
* @param array $property_conditions
|
||||
* Array of key => $value pairs passed into
|
||||
* tmgmt_entity_get_translatable_entities() as the second parameter.
|
||||
*
|
||||
* @return array
|
||||
* Array of entities.
|
||||
*/
|
||||
public function getEntitiesTranslationData($type, $property_conditions = array()) {
|
||||
|
||||
$return_value = array();
|
||||
$entities = tmgmt_entity_get_translatable_entities($type, $property_conditions, TRUE);
|
||||
|
||||
$entity_info = entity_get_info($type);
|
||||
$bundles = tmgmt_entity_get_translatable_bundles($type);
|
||||
|
||||
// For retrieved entities add translation specific data.
|
||||
foreach ($entities as $entity) {
|
||||
|
||||
list($entity_id, , $bundle) = entity_extract_ids($type, $entity);
|
||||
$entity_uri = entity_uri($type, $entity);
|
||||
|
||||
// This occurs on user entity type.
|
||||
if (empty($entity_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var EntityTranslationDefaultHandler $handler
|
||||
*/
|
||||
$handler = entity_translation_get_handler($type, $entity);
|
||||
|
||||
// Get existing translations and current job items for the entity
|
||||
// to determine translation statuses
|
||||
$translations = $handler->getTranslations();
|
||||
$source_lang = entity_language($type, $entity);
|
||||
$current_job_items = tmgmt_job_item_load_latest('entity', $type, $entity_id, $source_lang);
|
||||
|
||||
// Load basic entity data.
|
||||
$return_value[$entity_id] = array(
|
||||
'entity_type' => $type,
|
||||
'entity_id' => $entity_id,
|
||||
'entity_label' => entity_label($type, $entity),
|
||||
'entity_uri' => $entity_uri['path'],
|
||||
);
|
||||
|
||||
if (count($bundles) > 1) {
|
||||
$return_value[$entity_id]['bundle'] = isset($bundles[$bundle]) ? $bundles[$bundle] : t('Unknown');
|
||||
}
|
||||
|
||||
// Load entity translation specific data.
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
|
||||
$translation_status = 'current';
|
||||
|
||||
if ($langcode == $source_lang) {
|
||||
$translation_status = 'original';
|
||||
}
|
||||
elseif (!isset($translations->data[$langcode])) {
|
||||
$translation_status = 'missing';
|
||||
}
|
||||
elseif (!empty($translations->data[$langcode]['translate'])) {
|
||||
$translation_status = 'outofdate';
|
||||
}
|
||||
|
||||
$return_value[$entity_id]['current_job_items'][$langcode] = isset($current_job_items[$langcode]) ? $current_job_items[$langcode]: NULL;
|
||||
$return_value[$entity_id]['translation_statuses'][$langcode] = $translation_status;
|
||||
}
|
||||
}
|
||||
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds search form for entity sources overview.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*
|
||||
* @return array
|
||||
* Drupal form array.
|
||||
*/
|
||||
public function overviewSearchFormPart($form, &$form_state, $type) {
|
||||
|
||||
// Add search form specific styling.
|
||||
drupal_add_css(drupal_get_path('module', 'tmgmt_entity') . '/css/tmgmt_entity.admin.entity_source_search_form.css');
|
||||
|
||||
$form = array();
|
||||
// Add entity type value into form array so that it is available in
|
||||
// the form alter hook.
|
||||
$form_state['entity_type'] = $type;
|
||||
|
||||
$form['search_wrapper'] = array(
|
||||
'#prefix' => '<div class="tmgmt-sources-wrapper tmgmt-entity-sources-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -15,
|
||||
);
|
||||
$form['search_wrapper']['search'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
$form['search_wrapper']['search_submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Search'),
|
||||
'#weight' => 10,
|
||||
);
|
||||
$form['search_wrapper']['search_cancel'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Cancel'),
|
||||
'#weight' => 11,
|
||||
);
|
||||
|
||||
$entity_info = entity_get_info($type);
|
||||
|
||||
$label_key = isset($entity_info['entity keys']['label']) ? $entity_info['entity keys']['label'] : NULL;
|
||||
|
||||
if (!empty($label_key)) {
|
||||
$form['search_wrapper']['search'][$label_key] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('@entity_name title', array('@entity_name' => $entity_info['label'])),
|
||||
'#size' => 25,
|
||||
'#default_value' => isset($_GET[$label_key]) ? $_GET[$label_key] : NULL,
|
||||
);
|
||||
}
|
||||
|
||||
$language_options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$language_options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$form['search_wrapper']['search']['language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Source Language'),
|
||||
'#options' => $language_options,
|
||||
'#empty_option' => t('All'),
|
||||
'#default_value' => isset($_GET['language']) ? $_GET['language'] : NULL,
|
||||
);
|
||||
|
||||
$bundle_key = $entity_info['entity keys']['bundle'];
|
||||
$bundle_options = tmgmt_entity_get_translatable_bundles($type);
|
||||
|
||||
if (count($bundle_options) > 1) {
|
||||
$form['search_wrapper']['search'][$bundle_key] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('@entity_name type', array('@entity_name' => $entity_info['label'])),
|
||||
'#options' => $bundle_options,
|
||||
'#empty_option' => t('All'),
|
||||
'#default_value' => isset($_GET[$bundle_key]) ? $_GET[$bundle_key] : NULL,
|
||||
);
|
||||
}
|
||||
// In case entity translation is not enabled for any of bundles
|
||||
// display appropriate message.
|
||||
elseif (count($bundle_options) == 0) {
|
||||
drupal_set_message(t('Entity translation is not enabled for any of existing content types. To use this functionality go to Content types administration and enable entity translation for desired content types.'), 'warning');
|
||||
unset($form['search_wrapper']);
|
||||
}
|
||||
|
||||
$options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$form['search_wrapper']['search']['target_language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target language'),
|
||||
'#options' => $options,
|
||||
'#empty_option' => t('Any'),
|
||||
'#default_value' => isset($_GET['target_language']) ? $_GET['target_language'] : NULL,
|
||||
);
|
||||
$form['search_wrapper']['search']['target_status'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target status'),
|
||||
'#options' => array(
|
||||
'untranslated_or_outdated' => t('Untranslated or outdated'),
|
||||
'untranslated' => t('Untranslated'),
|
||||
'outdated' => t('Outdated'),
|
||||
),
|
||||
'#default_value' => isset($_GET['target_status']) ? $_GET['target_status'] : NULL,
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
':input[name="search[target_language]"]' => array('value' => ''),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs redirect with search params appended to the uri.
|
||||
*
|
||||
* In case of triggering element is edit-search-submit it redirects to
|
||||
* current location with added query string containing submitted search form
|
||||
* values.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*/
|
||||
public function overviewSearchFormRedirect($form, &$form_state, $type) {
|
||||
if ($form_state['triggering_element']['#id'] == 'edit-search-cancel') {
|
||||
drupal_goto($_GET['q']);
|
||||
}
|
||||
elseif ($form_state['triggering_element']['#id'] == 'edit-search-submit') {
|
||||
|
||||
$query = array();
|
||||
|
||||
foreach ($form_state['values']['search'] as $key => $value) {
|
||||
$query[$key] = $value;
|
||||
}
|
||||
|
||||
drupal_goto($_GET['q'], array('query' => $query));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_menu() {
|
||||
$items = parent::hook_menu();
|
||||
if (isset($items['admin/tmgmt/sources/entity_node'])) {
|
||||
// We assume that nodes are the most important overview if enabled, so
|
||||
// make sure they show up first.
|
||||
$items['admin/tmgmt/sources/entity_node']['weight'] = -20;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
name = Entity Source User Interface
|
||||
description = User Interface for the entity translation source plugin.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = tmgmt_entity
|
||||
dependencies[] = tmgmt_ui
|
||||
dependencies[] = views_bulk_operations
|
||||
|
||||
files[] = tmgmt_entity_ui.test
|
||||
files[] = tmgmt_entity_ui.list.test
|
||||
files[] = tmgmt_entity_ui.ui.inc
|
||||
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
|
||||
class TMGMTEntitySourceListTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
protected $nodes = array();
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source List tests',
|
||||
'description' => 'Tests the user interface for entity translation lists.',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity_ui', 'translation', 'comment', 'taxonomy'));
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('fr');
|
||||
|
||||
// Enable entity translations for nodes and comments.
|
||||
$edit = array();
|
||||
$edit['entity_translation_entity_types[comment]'] = 1;
|
||||
$edit['entity_translation_entity_types[node]'] = 1;
|
||||
$edit['entity_translation_entity_types[taxonomy_term]'] = 1;
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
$this->createNodeType('page', 'Page', TRANSLATION_ENABLED);
|
||||
|
||||
// Create nodes that will be used during tests.
|
||||
// NOTE that the order matters as results are read by xpath based on
|
||||
// position in the list.
|
||||
$this->nodes['page']['en'][] = $this->createNode('page');
|
||||
$this->nodes['article']['de'][0] = $this->createNode('article', 'de');
|
||||
$this->nodes['article']['fr'][0] = $this->createNode('article', 'fr');
|
||||
$this->nodes['article']['en'][3] = $this->createNode('article', 'en');
|
||||
$this->nodes['article']['en'][2] = $this->createNode('article', 'en');
|
||||
$this->nodes['article']['en'][1] = $this->createNode('article', 'en');
|
||||
$this->nodes['article']['en'][0] = $this->createNode('article', 'en');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the term bundle filter works.
|
||||
*/
|
||||
function testTermBundleFilter() {
|
||||
|
||||
$vocabulary1 = entity_create('taxonomy_vocabulary', array(
|
||||
'machine_name' => 'vocab1',
|
||||
'name' => $this->randomName(),
|
||||
));
|
||||
taxonomy_vocabulary_save($vocabulary1);
|
||||
|
||||
$term1 = entity_create('taxonomy_term', array(
|
||||
'name' => $this->randomName(),
|
||||
'vid' => $vocabulary1->vid,
|
||||
));
|
||||
taxonomy_term_save($term1);
|
||||
|
||||
$vocabulary2 = (object) array(
|
||||
'machine_name' => 'vocab2',
|
||||
'name' => $this->randomName(),
|
||||
);
|
||||
taxonomy_vocabulary_save($vocabulary2);
|
||||
|
||||
$term2 = entity_create('taxonomy_term', array(
|
||||
'name' => $this->randomName(),
|
||||
'vid' => $vocabulary2->vid,
|
||||
));
|
||||
taxonomy_term_save($term2);
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_taxonomy_term');
|
||||
// Both terms should be displayed with their bundle.
|
||||
$this->assertText($term1->name);
|
||||
$this->assertText($term2->name);
|
||||
$this->assertTrue($this->xpath('//td[text()=@vocabulary]', array('@vocabulary' => $vocabulary1->name)));
|
||||
$this->assertTrue($this->xpath('//td[text()=@vocabulary]', array('@vocabulary' => $vocabulary2->name)));
|
||||
|
||||
// Limit to the first vocabulary.
|
||||
$edit = array();
|
||||
$edit['search[vocabulary_machine_name]'] = $vocabulary1->machine_name;
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
// Only term 1 should be displayed now.
|
||||
$this->assertText($term1->name);
|
||||
$this->assertNoText($term2->name);
|
||||
$this->assertTrue($this->xpath('//td[text()=@vocabulary]', array('@vocabulary' => $vocabulary1->name)));
|
||||
$this->assertFalse($this->xpath('//td[text()=@vocabulary]', array('@vocabulary' => $vocabulary2->name)));
|
||||
|
||||
}
|
||||
|
||||
function testAvailabilityOfEntityLists() {
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_comment');
|
||||
// Check if we are at comments page.
|
||||
$this->assertText(t('Comment overview (Entity)'));
|
||||
// No comments yet - empty message is expected.
|
||||
$this->assertText(t('No entities matching given criteria have been found.'));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
// Check if we are at nodes page.
|
||||
$this->assertText(t('Node overview (Entity)'));
|
||||
// We expect article title as article node type is entity translatable.
|
||||
$this->assertText($this->nodes['article']['en'][0]->title);
|
||||
// Page node type should not be listed as it is not entity translatable.
|
||||
$this->assertNoText($this->nodes['page']['en'][0]->title);
|
||||
}
|
||||
|
||||
function testTranslationStatuses() {
|
||||
|
||||
// Test statuses: Source, Missing.
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
$langstatus_en = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr[1]/td[@class="langstatus-en"]');
|
||||
$langstatus_de = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr[1]/td[@class="langstatus-de"]');
|
||||
|
||||
$this->assertEqual($langstatus_en[0]->div['title'], t('Source language'));
|
||||
$this->assertEqual($langstatus_de[0]->div['title'], t('Not translated'));
|
||||
|
||||
// Test status: Active job item.
|
||||
$job = $this->createJob('en', 'de');
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$job->addItem('entity', 'node', $this->nodes['article']['en'][0]->nid);
|
||||
$job->requestTranslation();
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
$langstatus_de = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr[1]/td[@class="langstatus-de"]/a');
|
||||
|
||||
$items = $job->getItems();
|
||||
$wrapper = entity_metadata_wrapper('tmgmt_job_item', array_shift($items));
|
||||
$label = t('Active job item: @state', array('@state' => $wrapper->state->label()));
|
||||
|
||||
$this->assertEqual($langstatus_de[0]->div['title'], $label);
|
||||
|
||||
// Test status: Current
|
||||
foreach ($job->getItems() as $job_item) {
|
||||
$job_item->acceptTranslation();
|
||||
}
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
$langstatus_de = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr[1]/td[@class="langstatus-de"]');
|
||||
|
||||
$this->assertEqual($langstatus_de[0]->div['title'], t('Translation up to date'));
|
||||
}
|
||||
|
||||
function testTranslationSubmissions() {
|
||||
|
||||
// Simple submission.
|
||||
$nid = $this->nodes['article']['en'][0]->nid;
|
||||
$edit = array();
|
||||
$edit["items[$nid]"] = 1;
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Request translation'));
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
|
||||
// Submission of two entities of the same source language.
|
||||
$nid1 = $this->nodes['article']['en'][0]->nid;
|
||||
$nid2 = $this->nodes['article']['en'][1]->nid;
|
||||
$edit = array();
|
||||
$edit["items[$nid1]"] = 1;
|
||||
$edit["items[$nid2]"] = 1;
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Request translation'));
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
|
||||
// Submission of several entities of different source languages.
|
||||
$nid1 = $this->nodes['article']['en'][0]->nid;
|
||||
$nid2 = $this->nodes['article']['en'][1]->nid;
|
||||
$nid3 = $this->nodes['article']['en'][2]->nid;
|
||||
$nid4 = $this->nodes['article']['en'][3]->nid;
|
||||
$nid5 = $this->nodes['article']['de'][0]->nid;
|
||||
$nid6 = $this->nodes['article']['fr'][0]->nid;
|
||||
$edit = array();
|
||||
$edit["items[$nid1]"] = 1;
|
||||
$edit["items[$nid2]"] = 1;
|
||||
$edit["items[$nid3]"] = 1;
|
||||
$edit["items[$nid4]"] = 1;
|
||||
$edit["items[$nid5]"] = 1;
|
||||
$edit["items[$nid6]"] = 1;
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Request translation'));
|
||||
$this->assertText(t('@count jobs need to be checked out.', array('@count' => '3')));
|
||||
}
|
||||
|
||||
function testNodeEntityListings() {
|
||||
|
||||
// Turn off the entity translation.
|
||||
$edit = array();
|
||||
$edit['language_content_type'] = TRANSLATION_ENABLED;
|
||||
$this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
|
||||
|
||||
// Check if we have appropriate message in case there are no entity
|
||||
// translatable content types.
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
$this->assertText(t('Entity translation is not enabled for any of existing content types. To use this functionality go to Content types administration and enable entity translation for desired content types.'));
|
||||
|
||||
// Turn on the entity translation for both - article and page - to test
|
||||
// search form.
|
||||
$edit = array();
|
||||
$edit['language_content_type'] = ENTITY_TRANSLATION_ENABLED;
|
||||
$this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
|
||||
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
// Create page node after entity translation is enabled.
|
||||
$page_node_translatable = $this->createNode('page');
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
// We have both listed - one of articles and page.
|
||||
$this->assertText($this->nodes['article']['en'][0]->title);
|
||||
$this->assertText($page_node_translatable->title);
|
||||
|
||||
// Try the search by content type.
|
||||
$edit = array();
|
||||
$edit['search[type]'] = 'article';
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Search'));
|
||||
// There should be article present.
|
||||
$this->assertText($this->nodes['article']['en'][0]->title);
|
||||
// The page node should not be listed.
|
||||
$this->assertNoText($page_node_translatable->title);
|
||||
|
||||
// Try cancel button - despite we do post content type search value
|
||||
// we should get nodes of botch content types.
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Cancel'));
|
||||
$this->assertText($this->nodes['article']['en'][0]->title);
|
||||
$this->assertText($page_node_translatable->title);
|
||||
}
|
||||
|
||||
function testEntitySourceListSearch() {
|
||||
|
||||
// We need a node with title composed of several words to test
|
||||
// "any words" search.
|
||||
$title_part_1 = $this->randomName('4');
|
||||
$title_part_2 = $this->randomName('4');
|
||||
$title_part_3 = $this->randomName('4');
|
||||
|
||||
$this->nodes['article']['en'][0]->title = "$title_part_1 $title_part_2 $title_part_3";
|
||||
node_save($this->nodes['article']['en'][0]);
|
||||
|
||||
// Submit partial node title and see if we have a result.
|
||||
$edit = array();
|
||||
$edit['search[title]'] = "$title_part_1 $title_part_3";
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Search'));
|
||||
$this->assertText("$title_part_1 $title_part_2 $title_part_3", 'Searching on partial node title must return the result.');
|
||||
|
||||
// Check if there is only one result in the list.
|
||||
$search_result_rows = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr');
|
||||
$this->assert(count($search_result_rows) == 1, 'The search result must return only one row.');
|
||||
|
||||
// To test if other entity types work go for simple comment search.
|
||||
$comment = new stdClass();
|
||||
$comment->comment_body[LANGUAGE_NONE][0]['value'] = $this->randomName();
|
||||
$comment->subject = $this->randomName();
|
||||
// We need to associate the comment with entity translatable node object.
|
||||
$comment->nid = $this->nodes['article']['en'][0]->nid;
|
||||
// Set defaults - without these we will get Undefined property notices.
|
||||
$comment->is_anonymous = TRUE;
|
||||
$comment->cid = 0;
|
||||
$comment->pid = 0;
|
||||
$comment->uid = 0;
|
||||
// Will add further comment variables.
|
||||
$comment = comment_submit($comment);
|
||||
comment_save($comment);
|
||||
// Do search for the comment.
|
||||
$edit = array();
|
||||
$edit['search[subject]'] = $comment->subject;
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_comment', $edit, t('Search'));
|
||||
$this->assertText($comment->subject, 'Searching for a comment subject.');
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main module file for the translation management entity source plugin user
|
||||
* interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_page_alter().
|
||||
*/
|
||||
function tmgmt_entity_ui_page_alter(&$page) {
|
||||
if (entity_access('create', 'tmgmt_job')) {
|
||||
// Translation tabs for nodes.
|
||||
if (isset($page['content']['system_main']['entity_translation_overview'])) {
|
||||
module_load_include('inc', 'tmgmt_entity_ui', 'tmgmt_entity_ui.pages');
|
||||
$page['content']['system_main']['entity_translation_overview'] = drupal_get_form('tmgmt_entity_ui_translate_form', $page['content']['system_main']);
|
||||
}
|
||||
// Support the context module: when context is used for placing the
|
||||
// system_main block, then block contents appear nested one level deeper
|
||||
// under another 'content' key.
|
||||
elseif (isset($page['content']['system_main']['content']['entity_translation_overview'])) {
|
||||
module_load_include('inc', 'tmgmt_entity_ui', 'tmgmt_entity_ui.pages');
|
||||
$page['content']['system_main']['content']['entity_translation_overview'] = drupal_get_form('tmgmt_entity_ui_translate_form', $page['content']['system_main']['content']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements tmgmt_entity_tmgmt_source_plugin_info_alter().
|
||||
*/
|
||||
function tmgmt_entity_ui_tmgmt_source_plugin_info_alter(&$info) {
|
||||
|
||||
// Define ui controller class to handle Drupal entities.
|
||||
$info['entity']['ui controller class'] = 'TMGMTEntitySourceUIController';
|
||||
|
||||
// Alter file and file path info so that tmgmt_entity_ui module is targeted
|
||||
// for page callback.
|
||||
$info['entity']['file'] = 'tmgmt_entity_ui.pages.inc';
|
||||
$info['entity']['file path'] = drupal_get_path('module', 'tmgmt_entity_ui');
|
||||
}
|
||||
|
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides page and form callbacks for the Translation Management Tool Entity
|
||||
* Source User Interface module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Entity translation overview form.
|
||||
*/
|
||||
function tmgmt_entity_ui_translate_form($form, &$form_state, $build) {
|
||||
// Store the entity in the form state so we can easily create the job in the
|
||||
// submit handler.
|
||||
$form_state['entity_type'] = $build['#entity_type'];
|
||||
$form_state['entity'] = $build['#entity'];
|
||||
|
||||
$overview = $build['entity_translation_overview'];
|
||||
|
||||
list($id, $vid, $bundle) = entity_extract_ids($form_state['entity_type'], $form_state['entity']);
|
||||
|
||||
$form['top_actions']['#type'] = 'actions';
|
||||
$form['top_actions']['#weight'] = -10;
|
||||
tmgmt_ui_add_cart_form($form['top_actions'], $form_state, 'entity', $build['#entity_type'], $id);
|
||||
|
||||
// Inject our additional column into the header.
|
||||
array_splice($overview['#header'], -1, 0, array(t('Pending Translations')));
|
||||
// Make this a tableselect form.
|
||||
$form['languages'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $overview['#header'],
|
||||
'#options' => array(),
|
||||
);
|
||||
$languages = language_list();
|
||||
// Check if there is a job / job item that references this translation.
|
||||
$entity_language = entity_language($form_state['entity_type'], $form_state['entity']);
|
||||
$items = tmgmt_job_item_load_latest('entity', $form_state['entity_type'], $id, $entity_language);
|
||||
foreach ($languages as $langcode => $language) {
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
// Never show language neutral on the overview.
|
||||
continue;
|
||||
}
|
||||
// Since the keys are numeric and in the same order we can shift one element
|
||||
// after the other from the original non-form rows.
|
||||
$option = array_shift($overview['#rows']);
|
||||
if ($langcode == $entity_language) {
|
||||
$additional = '<strong>' . t('Source') . '</strong>';
|
||||
// This is the source object so we disable the checkbox for this row.
|
||||
$form['languages'][$langcode] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
elseif (isset($items[$langcode])) {
|
||||
$item = $items[$langcode];
|
||||
$uri = $item->uri();
|
||||
$wrapper = entity_metadata_wrapper('tmgmt_job_item', $item);
|
||||
$additional = l($wrapper->state->label(), $uri['path'], array('query' => array('destination' => current_path())));
|
||||
// Disable the checkbox for this row since there is already a translation
|
||||
// in progress that has not yet been finished. This way we make sure that
|
||||
// we don't stack multiple active translations for the same item on top
|
||||
// of each other.
|
||||
$form['languages'][$langcode] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
else {
|
||||
// There is no translation job / job item for this target language.
|
||||
$additional = t('None');
|
||||
}
|
||||
// Inject the additional column into the array.
|
||||
|
||||
// The generated form structure has changed, support both an additional
|
||||
// 'data' key (that is not supported by tableselect) and the old version
|
||||
// without.
|
||||
if (isset($option['data'])) {
|
||||
array_splice($option['data'], -1, 0, array($additional));
|
||||
// Append the current option array to the form.
|
||||
$form['languages']['#options'][$langcode] = $option['data'];
|
||||
}
|
||||
else {
|
||||
array_splice($option, -1, 0, array($additional));
|
||||
// Append the current option array to the form.
|
||||
$form['languages']['#options'][$langcode] = $option;
|
||||
}
|
||||
}
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['request'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Request translation'),
|
||||
'#submit' => array('tmgmt_entity_ui_translate_form_submit'),
|
||||
'#validate' => array('tmgmt_entity_ui_translate_form_validate'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback for the entity translation overview form.
|
||||
*/
|
||||
function tmgmt_entity_ui_translate_form_validate($form, &$form_state) {
|
||||
$selected = array_filter($form_state['values']['languages']);
|
||||
if (empty($selected)) {
|
||||
form_set_error('languages', t('You have to select at least one language for requesting a translation.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit callback for the entity translation overview form.
|
||||
*/
|
||||
function tmgmt_entity_ui_translate_form_submit($form, &$form_state) {
|
||||
$entity = $form_state['entity'];
|
||||
$entity_type = $form_state['entity_type'];
|
||||
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
$values = $form_state['values'];
|
||||
$jobs = array();
|
||||
foreach (array_keys(array_filter($values['languages'])) as $langcode) {
|
||||
// Create the job object.
|
||||
$job = tmgmt_job_create(entity_language($entity_type, $entity), $langcode, $GLOBALS['user']->uid);
|
||||
try {
|
||||
// Add the job item.
|
||||
$job->addItem('entity', $entity_type, $id);
|
||||
// Append this job to the array of created jobs so we can redirect the user
|
||||
// to a multistep checkout form if necessary.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
watchdog_exception('tmgmt', $e);
|
||||
$languages = language_list();
|
||||
$target_lang_name = $languages[$langcode]->language;
|
||||
drupal_set_message(t('Unable to add job item for target language %name. Make sure the source content is not empty.', array('%name' => $target_lang_name)), 'error');
|
||||
}
|
||||
}
|
||||
tmgmt_ui_job_checkout_and_redirect($form_state, $jobs);
|
||||
}
|
@@ -0,0 +1,328 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Node Source tests.
|
||||
*
|
||||
*/
|
||||
class TMGMTEntitySourceUITestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source UI tests',
|
||||
'description' => 'Tests the user interface for entity translation sources.',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('entity_translation'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity_ui', 'block', 'comment'));
|
||||
variable_set('language_content_type_page', ENTITY_TRANSLATION_ENABLED);
|
||||
variable_set('language_content_type_article', ENTITY_TRANSLATION_ENABLED);
|
||||
|
||||
$this->loginAsAdmin(array(
|
||||
'create translation jobs',
|
||||
'submit translation jobs',
|
||||
'accept translation jobs',
|
||||
'administer blocks',
|
||||
'administer entity translation',
|
||||
'toggle field translatability',
|
||||
));
|
||||
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('fr');
|
||||
$this->setEnvironment('es');
|
||||
$this->setEnvironment('el');
|
||||
|
||||
$this->createNodeType('page', st('Page'), ENTITY_TRANSLATION_ENABLED);
|
||||
$this->createNodeType('article', st('Article'), ENTITY_TRANSLATION_ENABLED);
|
||||
|
||||
// Enable path locale detection.
|
||||
$edit = array(
|
||||
'language[enabled][locale-url]' => TRUE,
|
||||
'language_content[enabled][locale-interface]' => TRUE,
|
||||
);
|
||||
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
|
||||
|
||||
// @todo Re-enable this when switching to testing profile.
|
||||
// Enable the main page content block for hook_page_alter() to work.
|
||||
$edit = array(
|
||||
'blocks[system_main][region]' => 'content',
|
||||
);
|
||||
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testNodeTranslateTabSingleCheckout() {
|
||||
|
||||
$this->loginAsTranslator(array('translate node entities'));
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->createNode('page', 'en');
|
||||
// Create a nodes that will not be translated to test the missing
|
||||
// translation filter.
|
||||
$node_not_translated = $this->createNode('page', 'en');
|
||||
$node_german = $this->createNode('page', 'de');
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
|
||||
// Submit.
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('German'))));
|
||||
|
||||
// Verify that the pending translation is shown.
|
||||
$this->clickLink(t('Needs review'));
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node->title)));
|
||||
|
||||
// German node should now be listed and be clickable.
|
||||
// @todo Improve detection of the link, e.g. use xpath on the table or the
|
||||
// title module to get a better title.
|
||||
$this->clickLink('view', 1);
|
||||
$this->assertText('de_' . $node->body['en'][0]['value']);
|
||||
|
||||
// Test that the destination query argument does not break the redirect
|
||||
// and we are redirected back to the correct page.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate', array('query' => array('destination' => 'node')));
|
||||
|
||||
// Request a spanish translation.
|
||||
$edit = array(
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the checkout page.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the originally defined destination URL.
|
||||
$this->assertEqual(url('node', array('absolute' => TRUE)), $this->getUrl());
|
||||
|
||||
// Test the missing translation filter.
|
||||
$this->drupalGet('admin/tmgmt/sources');
|
||||
$this->assertText($node->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
$this->drupalPost(NULL, array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated',
|
||||
), t('Search'));
|
||||
$this->assertNoText($node->title);
|
||||
$this->assertNoText($node_german->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
// Update the the translate flag of the translated node and test if it is
|
||||
// listed among sources with missing translation.
|
||||
db_update('entity_translation')->fields(array('translate' => 1))
|
||||
->condition('entity_type', 'node')->condition('entity_id', $node->nid)->execute();
|
||||
$this->drupalPost(NULL, array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'outdated',
|
||||
), t('Search'));
|
||||
$this->assertText($node->title);
|
||||
$this->assertNoText($node_german->title);
|
||||
$this->assertNoText($node_not_translated->title);
|
||||
|
||||
$this->drupalPost(NULL, array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated_or_outdated',
|
||||
), t('Search'));
|
||||
$this->assertText($node->title);
|
||||
$this->assertNoText($node_german->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testNodeTranslateTabMultipeCheckout() {
|
||||
// Allow auto-accept.
|
||||
$default_translator = tmgmt_translator_load('test_translator');
|
||||
$default_translator->settings = array(
|
||||
'auto_accept' => TRUE,
|
||||
);
|
||||
$default_translator->save();
|
||||
|
||||
$this->loginAsTranslator(array('translate node entities'));
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->createNode('page', 'en');
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('2 jobs need to be checked out.'));
|
||||
|
||||
// Submit all jobs.
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator and continue'));
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('Spanish'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node->title)));
|
||||
|
||||
// Translated nodes should now be listed and be clickable.
|
||||
// @todo Use links on translate tab.
|
||||
$this->drupalGet('de/node/' . $node->nid);
|
||||
$this->assertText('de_' . $node->body['en'][0]['value']);
|
||||
|
||||
$this->drupalGet('es/node/' . $node->nid);
|
||||
$this->assertText('es_' . $node->body['en'][0]['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test translating comments.
|
||||
*
|
||||
* @todo: Disabled pending resolution of http://drupal.org/node/1760270.
|
||||
*/
|
||||
function dtestCommentTranslateTab() {
|
||||
|
||||
// Login as admin to be able to submit config page.
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
// Enable comment translation.
|
||||
$edit = array(
|
||||
'entity_translation_entity_types[comment]' => TRUE
|
||||
);
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
|
||||
// Change comment_body field to be translatable.
|
||||
$comment_body = field_info_field('comment_body');
|
||||
$comment_body['translatable'] = TRUE;
|
||||
field_update_field($comment_body);
|
||||
|
||||
// Create a user that is allowed to translate comments.
|
||||
$permissions = array('translate comment entities', 'create translation jobs', 'submit translation jobs', 'accept translation jobs', 'post comments', 'skip comment approval', 'edit own comments', 'access comments');
|
||||
$entity_translation_permissions = entity_translation_permission();
|
||||
// The new translation edit form of entity_translation requires a new
|
||||
// permission that does not yet exist in older versions. Add it
|
||||
// conditionally.
|
||||
if (isset($entity_translation_permissions['edit original values'])) {
|
||||
$permissions[] = 'edit original values';
|
||||
}
|
||||
$this->loginAsTranslator($permissions, TRUE);
|
||||
|
||||
// Create an english source term.
|
||||
$node = $this->createNode('article', 'en');
|
||||
|
||||
// Add a comment.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$edit = array(
|
||||
'subject' => $this->randomName(),
|
||||
'comment_body[en][0][value]' => $this->randomName(),
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('Your comment has been posted.'));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->clickLink('edit');
|
||||
$this->assertTrue(preg_match('|comment/(\d+)/edit$|', $this->getUrl(), $matches), 'Comment found');
|
||||
$comment = comment_load($matches[1]);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $comment->subject)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('2 jobs need to be checked out.'));
|
||||
|
||||
// Submit all jobs.
|
||||
$this->assertText($comment->subject);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator and continue'));
|
||||
$this->assertText($comment->subject);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('comment/' . $comment->cid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $comment->subject, '@language' => t('Spanish'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $comment->subject)));
|
||||
|
||||
// @todo Use links on translate tab.
|
||||
$this->drupalGet('de/comment/' . $comment->cid);
|
||||
$this->assertText('de_' . $comment->comment_body['en'][0]['value']);
|
||||
|
||||
// @todo Use links on translate tab.
|
||||
$this->drupalGet('es/node/' . $comment->cid);
|
||||
$this->assertText('es_' . $comment->comment_body['en'][0]['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the entity source specific cart functionality.
|
||||
*/
|
||||
function testCart() {
|
||||
$this->loginAsTranslator(array('translate node entities'));
|
||||
|
||||
$nodes = array();
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$nodes[$i] = $this->createNode('page');
|
||||
}
|
||||
|
||||
// Test the source overview.
|
||||
$this->drupalGet('admin/tmgmt/sources/entity');
|
||||
$this->drupalPost('admin/tmgmt/sources/entity', array(
|
||||
'items[' . $nodes[1]->nid . ']' => TRUE,
|
||||
'items[' . $nodes[2]->nid . ']' => TRUE,
|
||||
), t('Add to cart'));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/cart');
|
||||
$this->assertText($nodes[1]->title);
|
||||
$this->assertText($nodes[2]->title);
|
||||
|
||||
// Test the translate tab.
|
||||
$this->drupalGet('node/' . $nodes[3]->nid . '/translate');
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a>.',
|
||||
array('@count' => 2, '@url' => url('admin/tmgmt/cart'))));
|
||||
|
||||
$this->drupalPost(NULL, array(), t('Add to cart'));
|
||||
$this->assertRaw(t('@count content source was added into the <a href="@url">cart</a>.', array('@count' => 1, '@url' => url('admin/tmgmt/cart'))));
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a> including the current item.',
|
||||
array('@count' => 3, '@url' => url('admin/tmgmt/cart'))));
|
||||
}
|
||||
}
|
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Generic entity ui controller class for source plugin.
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
class TMGMTEntitySourceUIController extends TMGMTEntityDefaultSourceUIController {
|
||||
|
||||
/**
|
||||
* Gets overview form header.
|
||||
*
|
||||
* @return array
|
||||
* Header array definition as expected by theme_tablesort().
|
||||
*/
|
||||
public function overviewFormHeader($type) {
|
||||
$languages = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$languages['langcode-' . $langcode] = array(
|
||||
'data' => check_plain($language->name),
|
||||
);
|
||||
}
|
||||
|
||||
$entity_info = entity_get_info($type);
|
||||
|
||||
$header = array(
|
||||
'title' => array('data' => t('Title (in source language)')),
|
||||
);
|
||||
|
||||
// Show the bundle if there is more than one for this entity type.
|
||||
if (count(tmgmt_entity_get_translatable_bundles($type)) > 1) {
|
||||
$header['bundle'] = array('data' => t('@entity_name type', array('@entity_name' => $entity_info['label'])));
|
||||
}
|
||||
|
||||
$header += $languages;
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a table row for overview form.
|
||||
*
|
||||
* @param array $data
|
||||
* Data needed to build the list row.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function overviewRow($data) {
|
||||
$label = $data['entity_label'] ? $data['entity_label'] : t('@type: @id', array('@type' => $data['entity_type'], '@id' => $data['entity_id']));
|
||||
|
||||
$row = array(
|
||||
'id' => $data['entity_id'],
|
||||
'title' => l($label, $data['entity_uri']),
|
||||
);
|
||||
|
||||
if (isset($data['bundle'])) {
|
||||
$row['bundle'] = $data['bundle'];
|
||||
}
|
||||
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$row['langcode-' . $langcode] = array(
|
||||
'data' => theme('tmgmt_ui_translation_language_status_single', array(
|
||||
'translation_status' => $data['translation_statuses'][$langcode],
|
||||
'job_item' => isset($data['current_job_items'][$langcode]) ? $data['current_job_items'][$langcode] : NULL,
|
||||
)),
|
||||
'class' => array('langstatus-' . $langcode),
|
||||
);
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewForm($form, &$form_state, $type) {
|
||||
|
||||
$form += $this->overviewSearchFormPart($form, $form_state, $type);
|
||||
|
||||
$form['items'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $this->overviewFormHeader($type),
|
||||
'#empty' => t('No entities matching given criteria have been found.'),
|
||||
'#attributes' => array('id' => 'tmgmt-entities-list'),
|
||||
);
|
||||
|
||||
// Load search property params which will be passed into
|
||||
$search_property_params = array();
|
||||
$exclude_params = array('q', 'page');
|
||||
foreach ($_GET as $key => $value) {
|
||||
// Skip exclude params, and those that have empty values, as these would
|
||||
// make it into query condition instead of being ignored.
|
||||
if (in_array($key, $exclude_params) || $value === '') {
|
||||
continue;
|
||||
}
|
||||
$search_property_params[$key] = $value;
|
||||
}
|
||||
|
||||
foreach ($this->getEntitiesTranslationData($type, $search_property_params) as $data) {
|
||||
$form['items']['#options'][$data['entity_id']] = $this->overviewRow($data);
|
||||
}
|
||||
|
||||
$form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewFormValidate($form, &$form_state, $type) {
|
||||
if (!empty($form_state['values']['search']['target_language']) && $form_state['values']['search']['language'] == $form_state['values']['search']['target_language']) {
|
||||
form_set_error('search[target_language]', t('The source and target languages must not be the same.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewFormSubmit($form, &$form_state, $type) {
|
||||
|
||||
// Handle search redirect.
|
||||
$this->overviewSearchFormRedirect($form, $form_state, $type);
|
||||
|
||||
$jobs = array();
|
||||
$entities = entity_load($type, $form_state['values']['items']);
|
||||
$source_lang_registry = array();
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($entities as $entity) {
|
||||
/**
|
||||
* @var EntityTranslationDefaultHandler $handler
|
||||
*/
|
||||
$handler = entity_translation_get_handler($type, $entity);
|
||||
$source_lang = entity_language($type, $entity);
|
||||
list($entity_id, ,) = entity_extract_ids($type, $entity);
|
||||
|
||||
try {
|
||||
|
||||
// For given source lang no job exists yet.
|
||||
if (!isset($source_lang_registry[$source_lang])) {
|
||||
// Create new job.
|
||||
$job = tmgmt_job_create($source_lang, NULL, $GLOBALS['user']->uid);
|
||||
// Add initial job item.
|
||||
$job->addItem('entity', $type, $entity_id);
|
||||
// Add job identifier into registry
|
||||
$source_lang_registry[$source_lang] = $job->tjid;
|
||||
// Add newly created job into jobs queue.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
// We have a job for given source lang, so just add new job item for the
|
||||
// existing job.
|
||||
else {
|
||||
$jobs[$source_lang_registry[$source_lang]]->addItem('entity', $type, $entity_id);
|
||||
}
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
watchdog_exception('tmgmt', $e);
|
||||
$entity_label = entity_label($type, $entity);
|
||||
drupal_set_message(t('Unable to add job item for entity %name: %error.', array('%name' => $entity_label, '%error' => $e->getMessage())), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// If necessary, do a redirect.
|
||||
$redirects = tmgmt_ui_job_checkout_multiple($jobs);
|
||||
if ($redirects) {
|
||||
tmgmt_ui_redirect_queue_set($redirects, current_path());
|
||||
$form_state['redirect'] = tmgmt_ui_redirect_queue_dequeue();
|
||||
|
||||
drupal_set_message(format_plural(count($redirects), t('One job needs to be checked out.'), t('@count jobs need to be checked out.')));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* API documentation for this module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter source field data before being saved in a translation job.
|
||||
*
|
||||
* @param array $data
|
||||
* Source field data.
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param object $entity
|
||||
* An entity object.
|
||||
* @param string $langcode
|
||||
* The language of retrieved field values.
|
||||
*/
|
||||
function hook_tmgmt_field_source_data_alter(&$data, $entity_type, $entity, $langcode) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract translatable text elements from a field.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of $entity.
|
||||
* @param $entity
|
||||
* The entity being extracted.
|
||||
* @param $field
|
||||
* The field structure.
|
||||
* @param $instance
|
||||
* The field instance.
|
||||
* @param $langcode
|
||||
* The language associated with $items.
|
||||
* @param $items
|
||||
* Array of values for this field.
|
||||
*
|
||||
* @return
|
||||
* An array of translatable text elements, keyed by the schema name of the
|
||||
* field.
|
||||
*
|
||||
* @see text_tmgmt_source_translation_structure()
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
function hook_tmgmt_source_translation_structure($entity_type, $entity, $field, $instance, $langcode, $items) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts data on the entity of the field type owned by the module.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of $entity.
|
||||
* @param $entity
|
||||
* The entity being extracted.
|
||||
* @param $field
|
||||
* The field structure.
|
||||
* @param $instance
|
||||
* The field instance.
|
||||
* @param $langcode
|
||||
* The language associated with $items.
|
||||
* @param $data
|
||||
* Translated data array.
|
||||
* @param $use_field_translation
|
||||
* TRUE if field translation is being used.
|
||||
*
|
||||
* @see tmgmt_field_populate_entity()
|
||||
*/
|
||||
function hook_tmgmt_field_type_populate_entity($entity_type, $entity, $field, $instance, $langcode, $data, $use_field_translation) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter an entity object before populating its field with translations.
|
||||
*
|
||||
* @param array $data
|
||||
* Source field data.
|
||||
* @param object $entity
|
||||
* An entity object.
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param string $langcode
|
||||
* The language of retrieved field values.
|
||||
*/
|
||||
function hook_tmgmt_field_pre_populate_entity_alter(&$data, $entity, $entity_type, $langcode) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter an entity object after populating its field with translations.
|
||||
*
|
||||
* @param object $entity
|
||||
* An entity object.
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param array $data
|
||||
* Source field data.
|
||||
* @param string $langcode
|
||||
* The language of retrieved field values.
|
||||
*/
|
||||
function hook_tmgmt_field_post_populate_entity_alter(&$entity, $entity_type, $data, $langcode) {
|
||||
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
name = Translation Management Field
|
||||
description = Implements some hooks on behalf of the core Field modules that are required in the Translation Management module.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
test_dependencies[] = field_collection
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main module file for the Translation Management Source Field module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_translation_structure().
|
||||
*
|
||||
* This hook is implemented on behalf of the core text module.
|
||||
*/
|
||||
function text_tmgmt_source_translation_structure($entity_type, $entity, $field, $instance, $langcode, $items) {
|
||||
$structure = array();
|
||||
if (!empty($items)) {
|
||||
$structure['#label'] = check_plain($instance['label']);
|
||||
foreach ($items as $delta => $value) {
|
||||
$structure[$delta]['#label'] = t('Delta #@delta', array('@delta' => $delta));
|
||||
$structure[$delta]['value'] = array(
|
||||
'#label' => $structure['#label'],
|
||||
'#text' => $value['value'],
|
||||
'#translate' => TRUE,
|
||||
);
|
||||
// Add format.
|
||||
$structure[$delta]['format'] = array(
|
||||
'#label' => '',
|
||||
'#text' => $value['format'],
|
||||
'#translate' => FALSE,
|
||||
);
|
||||
if ($field['type'] == 'text_with_summary' && !empty($value['summary'])) {
|
||||
$structure[$delta]['summary'] = array(
|
||||
'#label' => t('Summary'),
|
||||
'#text' => $value['summary'],
|
||||
'#translate' => TRUE,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for retrieving all translatable field values from an entity.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type.
|
||||
* @param $entity
|
||||
* An entity object.
|
||||
* @param $langcode
|
||||
* The language of retrieved field values.
|
||||
* @param $only_translatable
|
||||
* If TRUE, only the fields which are flagged as translatable are returned.
|
||||
* Defaults to FALSE, which is usually used for node translation, where the
|
||||
* field translatability does not matter.
|
||||
*
|
||||
* @return array
|
||||
* The structured field data for all translatable fields
|
||||
*/
|
||||
function tmgmt_field_get_source_data($entity_type, $entity, $langcode, $only_translatable = FALSE) {
|
||||
try {
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
watchdog_exception('tmgmt field', $e);
|
||||
return array();
|
||||
}
|
||||
|
||||
$fields = array();
|
||||
foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) {
|
||||
$field = field_info_field($field_name);
|
||||
$items = field_get_items($entity_type, $entity, $field_name, $langcode);
|
||||
if ((!$only_translatable || $field['translatable']) && $items) {
|
||||
if ($data = module_invoke($field['module'], 'tmgmt_source_translation_structure', $entity_type, $entity, $field, $instance, $langcode, $items)) {
|
||||
$fields[$field_name] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drupal_alter('tmgmt_field_source_data', $fields, $entity_type, $entity, $langcode);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates a field on an object with the provided field values.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of $entity.
|
||||
* @param $entity
|
||||
* The object to be populated.
|
||||
* @param $langcode
|
||||
* The field language.
|
||||
* @param $data
|
||||
* An array of values.
|
||||
* @param $use_field_translation
|
||||
* TRUE if field translation is being used.
|
||||
*/
|
||||
function tmgmt_field_populate_entity($entity_type, $entity, $langcode, $data, $use_field_translation = TRUE) {
|
||||
drupal_alter('tmgmt_field_pre_populate_entity', $data, $entity, $entity_type, $langcode);
|
||||
|
||||
foreach (element_children($data) as $field_name) {
|
||||
if ($field = field_info_field($field_name)) {
|
||||
$function = $field['module'] . '_field_type_tmgmt_populate_entity';
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
$instance = field_info_instance($entity_type, $field_name, $bundle);
|
||||
if (function_exists($function)) {
|
||||
$function($entity_type, $entity, $field, $instance, $langcode, $data, $use_field_translation);
|
||||
}
|
||||
else {
|
||||
$field_langcode = $field['translatable'] ? $langcode : LANGUAGE_NONE;
|
||||
// When not using field translation, make sure we're not storing
|
||||
// multiple languages.
|
||||
if (!$use_field_translation) {
|
||||
$entity->{$field_name} = array($field_langcode => array());
|
||||
}
|
||||
|
||||
foreach (element_children($data[$field_name]) as $delta) {
|
||||
$columns = array();
|
||||
foreach (element_children($data[$field_name][$delta]) as $column) {
|
||||
if (isset($data[$field_name][$delta][$column]['#translation']['#text'])) {
|
||||
$columns[$column] = $data[$field_name][$delta][$column]['#translation']['#text'];
|
||||
}
|
||||
// For elements which are not translatable, keep using the original
|
||||
// value.
|
||||
elseif (isset($data[$field_name][$delta][$column]['#translate']) && $data[$field_name][$delta][$column]['#translate'] == FALSE) {
|
||||
$columns[$column] = $data[$field_name][$delta][$column]['#text'];
|
||||
}
|
||||
}
|
||||
// Make sure the array_merge() gets an array as a first parameter.
|
||||
if (!isset($entity->{$field_name}[$field_langcode][$delta])) {
|
||||
$entity->{$field_name}[$field_langcode][$delta] = array();
|
||||
}
|
||||
$entity->{$field_name}[$field_langcode][$delta] = array_merge($entity->{$field_name}[$field_langcode][$delta], $columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drupal_alter('tmgmt_field_post_populate_entity', $entity, $entity_type, $data, $langcode);
|
||||
}
|
||||
|
@@ -0,0 +1,20 @@
|
||||
name = i18n String Source
|
||||
description = i18n String source plugin for the Translation Management system.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
dependencies[] = tmgmt_ui
|
||||
dependencies[] = i18n_string
|
||||
# List variable as a dependency so that it gets picked up testbot.
|
||||
# See http://drupal.org/node/1440484
|
||||
dependencies[] = i18n
|
||||
dependencies[] = variable
|
||||
files[] = tmgmt_i18n_string.plugin.inc
|
||||
files[] = tmgmt_i18n_string.test
|
||||
files[] = tmgmt_i18n_string.ui.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Source plugin for the Translation Management system that handles i18n strings.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_plugin_info().
|
||||
*/
|
||||
function tmgmt_i18n_string_tmgmt_source_plugin_info() {
|
||||
$info['i18n_string'] = array(
|
||||
'label' => t('i18n String'),
|
||||
'description' => t('Source handler for i18n strings.'),
|
||||
'plugin controller class' => 'TMGMTI18nStringSourcePluginController',
|
||||
'ui controller class' => 'TMGMTI18nStringDefaultSourceUIController',
|
||||
);
|
||||
foreach (i18n_object_info() as $object_type => $object_info) {
|
||||
// Only consider object types that have string translation information.
|
||||
if (isset($object_info['string translation'])) {
|
||||
$info['i18n_string']['item types'][$object_type] = $object_info['title'];
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets i18n strings for given type and label.
|
||||
*
|
||||
* @param string $type
|
||||
* i18n object type.
|
||||
* @param string $search_label
|
||||
* Label to search for.
|
||||
* @param string $target_language
|
||||
* Target language.
|
||||
* @param string $target_status
|
||||
* Target status.
|
||||
*
|
||||
* @return array
|
||||
* List of i18n strings data.
|
||||
*/
|
||||
function tmgmt_i18n_string_get_strings($type, $search_label = NULL, $target_language = NULL, $target_status = 'untranslated_or_outdated') {
|
||||
$info = i18n_object_info($type);
|
||||
|
||||
$languages = drupal_map_assoc(array_keys(language_list()));
|
||||
$select = db_select('i18n_string', 'i18n_s');
|
||||
|
||||
$select->addTag('tmgmt_sources_search');
|
||||
$select->addMetaData('plugin', 'i18n_string');
|
||||
$select->addMetaData('type', $type);
|
||||
|
||||
$select->condition('i18n_s.textgroup', $info['string translation']['textgroup']);
|
||||
|
||||
if (!empty($target_language) && in_array($target_language, $languages)) {
|
||||
if ($target_status == 'untranslated_or_outdated') {
|
||||
$or = db_or();
|
||||
$or->isNull("lt_$target_language.language");
|
||||
$or->condition("lt_$target_language.i18n_status", I18N_STRING_STATUS_UPDATE);
|
||||
$select->condition($or);
|
||||
}
|
||||
elseif ($target_status == 'outdated') {
|
||||
$select->condition("lt_$target_language.i18n_status", I18N_STRING_STATUS_UPDATE);
|
||||
}
|
||||
elseif ($target_status == 'untranslated') {
|
||||
$select->isNull("lt_$target_language.language");
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($info['string translation']['type'])) {
|
||||
$select->condition('i18n_s.type', $info['string translation']['type']);
|
||||
}
|
||||
elseif ($type == 'field' || $type == 'field_instance') {
|
||||
// Fields and field instances share the same textgroup. Use list of bundles
|
||||
// to include/exclude field_instances.
|
||||
$bundles = array();
|
||||
foreach (entity_get_info() as $entity_info) {
|
||||
$bundles = array_merge($bundles, array_keys($entity_info['bundles']));
|
||||
}
|
||||
$select->condition('i18n_s.objectid', $bundles, $type == 'field_instance' ? 'IN' : 'NOT IN');
|
||||
}
|
||||
|
||||
$select->join('locales_source', 'ls', 'ls.lid = i18n_s.lid');
|
||||
$select->addField('ls', 'source');
|
||||
if (!empty($search_label)) {
|
||||
$select->condition('ls.source', "%$search_label%", 'LIKE');
|
||||
}
|
||||
|
||||
foreach ($languages as $langcode) {
|
||||
$langcode = str_replace('-', '', $langcode);
|
||||
$select->leftJoin('locales_target', "lt_$langcode", "i18n_s.lid = %alias.lid AND %alias.language = '$langcode'");
|
||||
$select->addField("lt_$langcode", 'language', "lang_$langcode");
|
||||
}
|
||||
$select->fields("i18n_s", array('lid', 'textgroup', 'context', 'type', 'objectid'));
|
||||
|
||||
$select->addExpression("concat(i18n_s.textgroup, ':', i18n_s.type, ':', i18n_s.objectid)", 'job_item_id');
|
||||
|
||||
$select->orderBy('i18n_s.context');
|
||||
|
||||
$select->groupBy('type');
|
||||
$select->groupBy('objectid');
|
||||
|
||||
$select = $select->extend('PagerDefault')->limit(variable_get('tmgmt_source_list_limit', 20));
|
||||
|
||||
return $select->execute()->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_ID_alter().
|
||||
*
|
||||
* Adds request translation capabilities into i18n translate tab.
|
||||
*/
|
||||
function tmgmt_i18n_string_form_i18n_string_translate_page_overview_form_alter(&$form, &$form_state) {
|
||||
$object = $form['object']['#value'];
|
||||
|
||||
// Create the id: textgroup:type:objectid.
|
||||
$id = $object->get_textgroup() . ':' . implode(':', $object->get_string_context());
|
||||
$source_language = variable_get_value('i18n_string_source_language');
|
||||
|
||||
$existing_items = tmgmt_job_item_load_latest('i18n_string', $object->get_type(), $id, $source_language);
|
||||
|
||||
$form['top_actions']['#type'] = 'actions';
|
||||
$form['top_actions']['#weight'] = -10;
|
||||
tmgmt_ui_add_cart_form($form['top_actions'], $form_state, 'i18n_string', $object->get_type(), $id);
|
||||
|
||||
$form['languages']['#type'] = 'tableselect';
|
||||
|
||||
// Append lang code so that we can use it
|
||||
foreach ($form['languages']['#rows'] as $lang => $row) {
|
||||
|
||||
if (isset($existing_items[$lang])) {
|
||||
|
||||
$states = tmgmt_job_item_states();
|
||||
$row['status'] = $states[$existing_items[$lang]->state];
|
||||
|
||||
if ($existing_items[$lang]->isNeedsReview()) {
|
||||
$row['operations'] .= ' | ' . l(t('review'), 'admin/tmgmt/items/' . $existing_items[$lang]->tjiid, array('query' => array('destination' => $_GET['q'])));
|
||||
}
|
||||
elseif ($existing_items[$lang]->isActive()) {
|
||||
$row['operations'] .= ' | ' . l(t('in progress'), 'admin/tmgmt/items/' . $existing_items[$lang]->tjiid, array('query' => array('destination' => $_GET['q'])));
|
||||
}
|
||||
}
|
||||
|
||||
$form['languages']['#options'][$id . ':' . $lang] = $row;
|
||||
|
||||
if ($lang == $source_language || isset($existing_items[$lang])) {
|
||||
$form['languages'][$id . ':' . $lang] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unset($form['languages']['#rows'], $form['languages']['#theme']);
|
||||
|
||||
$form['actions']['request_translation'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Request translation'),
|
||||
'#submit' => array('tmgmt_i18n_string_translate_form_submit'),
|
||||
'#validate' => array('tmgmt_i18n_string_translate_form_validate'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback for the entity translation overview form.
|
||||
*/
|
||||
function tmgmt_i18n_string_translate_form_validate($form, &$form_state) {
|
||||
$selected = array_filter($form_state['values']['languages']);
|
||||
if (empty($selected)) {
|
||||
form_set_error('languages', t('You have to select at least one language for requesting a translation.'));
|
||||
}
|
||||
}
|
||||
|
||||
function tmgmt_i18n_string_translate_form_submit($form, &$form_state) {
|
||||
|
||||
$items = array_filter($form_state['values']['languages']);
|
||||
$type = $form_state['values']['object']->get_type();
|
||||
$source_lang = variable_get_value('i18n_string_source_language');
|
||||
|
||||
$jobs = array();
|
||||
$target_lang_registry = array();
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($items as $item) {
|
||||
|
||||
$item_parts = explode(':', $item);
|
||||
$target_lang = array_pop($item_parts);
|
||||
$key = implode(':', $item_parts);
|
||||
|
||||
// For given source lang no job exists yet.
|
||||
if (!isset($target_lang_registry[$target_lang])) {
|
||||
// Create new job.
|
||||
$job = tmgmt_job_create($source_lang, $target_lang, $GLOBALS['user']->uid);
|
||||
// Add initial job item.
|
||||
$job->addItem('i18n_string', $type, $key);
|
||||
// Add job identifier into registry
|
||||
$target_lang_registry[$target_lang] = $job->tjid;
|
||||
// Add newly created job into jobs queue.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
// We have a job for given source lang, so just add new job item for the
|
||||
// existing job.
|
||||
else {
|
||||
$jobs[$target_lang_registry[$target_lang]]->addItem('i18n_string', $type, $key);
|
||||
}
|
||||
}
|
||||
tmgmt_ui_job_checkout_and_redirect($form_state, $jobs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_i18n_object_info_alter().
|
||||
*/
|
||||
function tmgmt_i18n_string_i18n_object_info_alter(&$info) {
|
||||
$entity_info = entity_get_info();
|
||||
// Add a entity key to the object info if neither load callback nor entity
|
||||
// keys are set and the object is an entity_type.
|
||||
// @todo: Add this as default in EntityDefaultI18nStringController.
|
||||
foreach ($info as $name => &$object) {
|
||||
if (!isset($object['load callback']) && !isset($object['entity']) && isset($entity_info[$name])) {
|
||||
$object['entity'] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the i18n wrapper object.
|
||||
*
|
||||
* I18N objects with one or two keys are supported.
|
||||
*
|
||||
* @param string $type
|
||||
* I18n object type.
|
||||
* @param object $i18n_string
|
||||
* Object with type and objectid properties.
|
||||
*
|
||||
* @return i18n_string_object_wrapper
|
||||
*/
|
||||
function tmgmt_i18n_string_get_wrapper($type, $i18n_string) {
|
||||
$object_key = i18n_object_info($type, 'key');
|
||||
|
||||
// Special handling for i18nviews.
|
||||
if ($type == 'views') {
|
||||
// The construct method needs the full view object.
|
||||
$view = views_get_view($i18n_string->objectid);
|
||||
$wrapper = i18n_get_object($type, $i18n_string->objectid, $view);
|
||||
return $wrapper;
|
||||
}
|
||||
|
||||
// Special handling for i18n_panels.
|
||||
$panels_objects = array(
|
||||
'pane_configuration' => 'panels_pane',
|
||||
'display_configuration' => 'panels_display',
|
||||
);
|
||||
if (in_array($type, array_keys($panels_objects))) {
|
||||
ctools_include('export');
|
||||
$wrapper = FALSE;
|
||||
switch ($type) {
|
||||
case 'display_configuration':
|
||||
$object_array = ctools_export_load_object($panels_objects[$type], 'conditions', array('uuid' => $i18n_string->objectid));
|
||||
$wrapper = i18n_get_object($type, $i18n_string->objectid, $object_array[$i18n_string->objectid]);
|
||||
break;
|
||||
|
||||
case 'pane_configuration':
|
||||
$obj = db_query("SELECT * FROM {panels_pane} WHERE uuid = :uuid", array(':uuid' => $i18n_string->objectid))->fetchObject();
|
||||
if ($obj) {
|
||||
$pane = ctools_export_unpack_object($panels_objects[$type], $obj);
|
||||
$translation = i18n_panels_get_i18n_translation_object($pane);
|
||||
$translation->uuid = $pane->uuid;
|
||||
$wrapper = i18n_get_object($type, $i18n_string->objectid, $translation);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return $wrapper;
|
||||
}
|
||||
|
||||
// Special handling if the object has two keys. Assume that they
|
||||
// mean type and object id.
|
||||
if ($type == 'field') {
|
||||
// Special case for fields which expect the type to be the identifier.
|
||||
$wrapper = i18n_get_object($type, $i18n_string->type);
|
||||
return $wrapper;
|
||||
}
|
||||
elseif ($type == 'field_instance') {
|
||||
// Special case for field instances, which use the field name as type and
|
||||
// bundle as object id. We don't know the entity_type, so we loop over all
|
||||
// entity_types to search for the bundle. This will clash if different
|
||||
// entity types have bundles with the same names.
|
||||
foreach (entity_get_info() as $entity_type => $entity_info) {
|
||||
if (isset($entity_info['bundles'][$i18n_string->objectid])) {
|
||||
list($type_key, $objectid_key) = $object_key;
|
||||
$wrapper = i18n_get_object($type, array(
|
||||
$type_key => $i18n_string->type,
|
||||
$objectid_key => $i18n_string->objectid
|
||||
), field_info_instance($entity_type, $i18n_string->type, $i18n_string->objectid));
|
||||
return $wrapper;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (count($object_key) == 2) {
|
||||
list($type_key, $objectid_key) = $object_key;
|
||||
$wrapper = i18n_get_object($type, array(
|
||||
$type_key => $i18n_string->type,
|
||||
$objectid_key => $i18n_string->objectid
|
||||
));
|
||||
return $wrapper;
|
||||
}
|
||||
else {
|
||||
// Otherwise, use the object id.
|
||||
$wrapper = i18n_get_object($type, $i18n_string->objectid);
|
||||
return $wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_suggestions()
|
||||
*/
|
||||
function tmgmt_i18n_string_tmgmt_source_suggestions(array $items, TMGMTJob $job) {
|
||||
$suggestions = array();
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (($item instanceof TMGMTJobItem) && ($item->item_type == 'node')) {
|
||||
// Load translatable menu items related to this node.
|
||||
$query = db_select('menu_links', 'ml')
|
||||
->condition('ml.link_path', 'node/' . $item->item_id)
|
||||
->fields('ml', array('mlid'));
|
||||
$query->join('menu_custom', 'mc', 'ml.menu_name = mc.menu_name AND mc.i18n_mode = ' . I18N_MODE_MULTIPLE);
|
||||
$results = $query->execute()->fetchAllAssoc('mlid');
|
||||
foreach ($results as $result) {
|
||||
$menu_link = menu_link_load($result->mlid);
|
||||
// Add suggestion.
|
||||
$suggestions[] = array(
|
||||
'job_item' => tmgmt_job_item_create('i18n_string', 'menu_link', "menu:item:{$result->mlid}"),
|
||||
'reason' => t('Menu link @title', array('@title' => $menu_link['link_title'])),
|
||||
'from_item' => $item->tjiid,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the i18n string source controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translation plugin controller for i18n strings.
|
||||
*/
|
||||
class TMGMTI18nStringSourcePluginController extends TMGMTDefaultSourcePluginController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
$i18n_object = $this->getI18nObjectWrapper($job_item);
|
||||
$structure = array();
|
||||
$languages = language_list();
|
||||
|
||||
if ($i18n_object instanceof i18n_string_object_wrapper) {
|
||||
$i18n_strings = $i18n_object->get_strings();
|
||||
$source_language = $job_item->getJob()->source_language;
|
||||
foreach ($i18n_strings as $string_id => $string) {
|
||||
// If the job source language is different from the i18n source language
|
||||
// try to load an existing translation for the language and use it as
|
||||
// the source.
|
||||
if ($source_language != i18n_string_source_language()) {
|
||||
$translation = $string->get_translation($source_language);
|
||||
|
||||
if (empty($translation)) {
|
||||
throw new TMGMTException(t('Unable to load %language translation for the string %title',
|
||||
array('%language' => $languages[$source_language]->name, '%title' => $string->title)));
|
||||
}
|
||||
// If '#label' is empty theme_tmgmt_ui_translator_review_form() fails.
|
||||
$structure[$string_id] = array(
|
||||
'#label' => !empty($string->title) ? $string->title : $string->property,
|
||||
'#text' => $translation,
|
||||
'#translate' => TRUE
|
||||
);
|
||||
}
|
||||
else {
|
||||
// If '#label' is empty theme_tmgmt_ui_translator_review_form() fails.
|
||||
$structure[$string_id] = array(
|
||||
'#label' => !empty($string->title) ? $string->title : $string->property,
|
||||
'#text' => $string->string,
|
||||
'#translate' => TRUE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item) {
|
||||
$job = tmgmt_job_load($job_item->tjid);
|
||||
$data = array_filter(tmgmt_flatten_data($job_item->getData()), '_tmgmt_filter_data');
|
||||
foreach ($data as $i18n_string => $item) {
|
||||
if (isset($item['#translation']['#text'])) {
|
||||
i18n_string_translation_update($i18n_string, $item['#translation']['#text'], $job->target_language);
|
||||
}
|
||||
}
|
||||
|
||||
// We just saved the translation, set the state of the job item to
|
||||
// 'finished'.
|
||||
$job_item->accepted();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
if ($i18n_object = $this->getI18nObjectWrapper($job_item)) {
|
||||
// Get the label, default to the get_title() method, fall back to the
|
||||
// first string if that is empty.
|
||||
$title = t('Unknown');
|
||||
if ($i18n_object->get_title()) {
|
||||
$title = $i18n_object->get_title();
|
||||
}
|
||||
elseif ($strings = $i18n_object->get_strings(array('empty' => TRUE))) {
|
||||
$title = reset($strings)->get_string();
|
||||
}
|
||||
return t('@title (@id)', array('@title' => strip_tags(drupal_substr($title, 0, 64)), '@id' => $job_item->item_id));
|
||||
}
|
||||
return parent::getLabel($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUri(TMGMTJobItem $job_item) {
|
||||
if ($wrapper = $this->getI18nObjectWrapper($job_item)) {
|
||||
return array(
|
||||
'path' => $wrapper->get_path(),
|
||||
'options' => array(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item) {
|
||||
if ($label = $this->getItemTypeLabel($job_item->item_type)) {
|
||||
return $label;
|
||||
}
|
||||
return parent::getType($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item) {
|
||||
return i18n_string_source_language();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
$existing_lang_codes = array();
|
||||
$languages = language_list();
|
||||
|
||||
if ($object = $this->getI18nObjectWrapper($job_item)) {
|
||||
$existing_lang_codes = array_keys($languages);
|
||||
foreach ($object->load_strings() as $string) {
|
||||
foreach ($languages as $language) {
|
||||
if ($language->language == $this->getSourceLangCode($job_item)) {
|
||||
continue;
|
||||
}
|
||||
// Remove languages for which we fail to find translation.
|
||||
if (in_array($language->language, $existing_lang_codes) && !$string->get_translation($language->language)) {
|
||||
$existing_lang_codes = array_diff($existing_lang_codes, array($language->language));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $existing_lang_codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get i18n_object_wrapper for given job item.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
*
|
||||
* @return i18n_string_object_wrapper
|
||||
*/
|
||||
protected function getI18nObjectWrapper(TMGMTJobItem $job_item) {
|
||||
list(, $type, $object_id) = explode(':', $job_item->item_id, 3);
|
||||
return tmgmt_i18n_string_get_wrapper($job_item->item_type, (object) array('type' => $type, 'objectid' => $object_id));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,496 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic i18n String Source tests.
|
||||
*/
|
||||
class TMGMTI18nStringSourceTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'i18n String Source tests',
|
||||
'description' => 'Exporting source data from i18n string and saving translations back',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('i18n_string'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_ui', 'tmgmt_i18n_string', 'taxonomy', 'i18n_taxonomy', 'i18n_block', 'i18n_field', 'list', 'i18n_menu'));
|
||||
$this->setEnvironment('de');
|
||||
$this->translator = $this->createTranslator();
|
||||
}
|
||||
|
||||
function testI18nStringSourceTaxonomy() {
|
||||
// Test translation of a vocabulary.
|
||||
/////////////////////////////////////
|
||||
$config = array(
|
||||
'name' => $this->randomName(),
|
||||
'machine_name' => 'test_vocab',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', $config);
|
||||
taxonomy_vocabulary_save($vocabulary);
|
||||
|
||||
$string_object_name = "taxonomy:vocabulary:" . $vocabulary->vid;
|
||||
$source_text = $vocabulary->name;
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item1 = $job->addItem('i18n_string', 'taxonomy_vocabulary', $string_object_name);
|
||||
$this->assertEqual(t('Vocabulary'), $item1->getSourceType());
|
||||
$job->requestTranslation();
|
||||
|
||||
foreach ($job->getItems() as $item) {
|
||||
/* @var $item TMGMTJobItem */
|
||||
$item->acceptTranslation();
|
||||
}
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $string_object_name, 'i18n Strings object correctly saved');
|
||||
|
||||
// Check string translation.
|
||||
$this->assertEqual(i18n_string_translate('taxonomy:vocabulary:' . $vocabulary->vid . ':name', $source_text, array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text);
|
||||
|
||||
// Test translation of a taxonomy term.
|
||||
/////////////////////////////////////
|
||||
$term = entity_create('taxonomy_term', array(
|
||||
'vid' => $vocabulary->vid,
|
||||
'name' => $this->randomName(),
|
||||
'description' => $this->randomName(),
|
||||
));
|
||||
taxonomy_term_save($term);
|
||||
|
||||
$string_object_name = "taxonomy:term:" . $term->tid;
|
||||
$source_text_name = $term->name;
|
||||
$source_text_description = $term->description;
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item1 = $job->addItem('i18n_string', 'taxonomy_term', $string_object_name);
|
||||
$this->assertEqual(t('Taxonomy term'), $item1->getSourceType());
|
||||
$job->requestTranslation();
|
||||
|
||||
/* @var $item TMGMTJobItem */
|
||||
foreach ($job->getItems() as $item) {
|
||||
// The source is available only in en.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
// The source should be now available in de and en.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $string_object_name);
|
||||
|
||||
// Check string translation.
|
||||
$this->assertEqual(i18n_string_translate('taxonomy:term:' . $term->tid . ':name', $source_text_name,
|
||||
array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text_name);
|
||||
$this->assertEqual(i18n_string_translate('taxonomy:term:' . $term->tid . ':description', $source_text_description,
|
||||
array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text_description);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the source is able to pull content in requested language.
|
||||
*/
|
||||
function testRequestDataForSpecificLanguage() {
|
||||
$this->setEnvironment('es');
|
||||
$this->setEnvironment('cs');
|
||||
|
||||
$config = array(
|
||||
'name' => $this->randomName(),
|
||||
'machine_name' => 'test_vocab',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', $config);
|
||||
taxonomy_vocabulary_save($vocabulary);
|
||||
|
||||
$string_object_name = "taxonomy:vocabulary:" . $vocabulary->vid;
|
||||
|
||||
i18n_string_translation_update($string_object_name . ':name', 'de translation', 'de');
|
||||
|
||||
// Create new job item with a source language for which the translation
|
||||
// exits.
|
||||
$job = $this->createJob('de', 'cs');
|
||||
$job->save();
|
||||
$job->addItem('i18n_string', 'taxonomy_vocabulary', $string_object_name);
|
||||
|
||||
$data = $job->getData();
|
||||
$this->assertEqual($data[1][$string_object_name . ':name']['#text'], 'de translation');
|
||||
|
||||
// Create new job item with a source language for which the translation
|
||||
// does not exit.
|
||||
$job = $this->createJob('es', 'cs');
|
||||
$job->save();
|
||||
try {
|
||||
$job->addItem('i18n_string', 'taxonomy_vocabulary', $string_object_name);
|
||||
$this->fail('The job item should not be added as there is no translation for language "es"');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$languages = language_list();
|
||||
$this->assertEqual(t('Unable to load %language translation for the string %title',
|
||||
array('%language' => $languages['es']->name, '%title' => 'Name')), $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function testI18nStringSourceMenu() {
|
||||
drupal_static_reset('_tmgmt_plugin_info');
|
||||
drupal_static_reset('_tmgmt_plugin_controller');
|
||||
|
||||
// Test translation of a menu.
|
||||
/////////////////////////////////////
|
||||
$config = array(
|
||||
'menu_name' => $this->randomName(),
|
||||
'title' => $this->randomName(),
|
||||
'description' => $this->randomName(),
|
||||
'i18n_mode' => I18N_MODE_MULTIPLE,
|
||||
);
|
||||
menu_save($config);
|
||||
$menu = menu_load($config['menu_name']);
|
||||
|
||||
$source_text = $menu['title'];
|
||||
$string_name = 'menu:menu:' . $menu['menu_name'];
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
|
||||
$item1 = $job->addItem('i18n_string', 'menu', $string_name);
|
||||
$this->assertEqual(t('Menu'), $item1->getSourceType());
|
||||
$job->requestTranslation();
|
||||
/* @var $item TMGMTJobItem */
|
||||
foreach ($job->getItems() as $item) {
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
|
||||
$data = $item1->getData();
|
||||
$this->assertEqual($data['menu:menu:' . $menu['menu_name'] . ':title']['#text'], $config['title']);
|
||||
$this->assertEqual($data['menu:menu:' . $menu['menu_name'] . ':description']['#text'], $config['description']);
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $string_name, 'String is correctly saved');
|
||||
|
||||
// Check string translation.
|
||||
$this->assertEqual(i18n_string_translate($string_name . ':title', $source_text, array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text);
|
||||
|
||||
// Test translation of a menu item.
|
||||
/////////////////////////////////////
|
||||
$source_text = $this->randomName();
|
||||
$menu_link = array(
|
||||
'link_path' => '<front>',
|
||||
'link_title' => $source_text,
|
||||
// i18n_menu_link::get_title() uses the title, set that too.
|
||||
'title' => $source_text,
|
||||
'menu_name' => $menu['menu_name'],
|
||||
'customized' => TRUE,
|
||||
);
|
||||
menu_link_save($menu_link);
|
||||
$string_name = 'menu:item:' . $menu_link['mlid'];
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
|
||||
$item1 = $job->addItem('i18n_string', 'menu_link', $string_name);
|
||||
$this->assertEqual(t('Menu link'), $item1->getSourceType());
|
||||
$job->requestTranslation();
|
||||
/* @var $item TMGMTJobItem */
|
||||
foreach ($job->getItems() as $item) {
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
|
||||
$data = $item1->getData();
|
||||
$this->assertEqual($data[$string_name . ':title']['#text'], $source_text);
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $string_name);
|
||||
|
||||
// Check string translation.
|
||||
$this->assertEqual(i18n_string_translate($string_name . ':title', $source_text, array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text);
|
||||
|
||||
}
|
||||
|
||||
function testI18nStringSourceLangCodes() {
|
||||
$config = array(
|
||||
'name' => $this->randomName(),
|
||||
'description' => 'description_' . $this->randomName(),
|
||||
'machine_name' => 'test_vocab',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', $config);
|
||||
taxonomy_vocabulary_save($vocabulary);
|
||||
|
||||
$string_object_name = "taxonomy:vocabulary:" . $vocabulary->vid;
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item = $job->addItem('i18n_string', 'taxonomy_vocabulary', $string_object_name);
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
|
||||
i18n_string_translation_update($string_object_name . ':description', 'de_' . $config['description'], 'de');
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
|
||||
i18n_string_translation_update($string_object_name . ':name', 'de_' . $config['name'], 'de');
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en', 'de'));
|
||||
}
|
||||
|
||||
function testI18nStringPluginUI() {
|
||||
|
||||
$this->loginAsAdmin(array('administer taxonomy', 'translate interface', 'translate user-defined strings'));
|
||||
|
||||
$vocab_data = array(
|
||||
'name' => $this->randomName(),
|
||||
'machine_name' => 'test_vocab',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
$term_data = array(
|
||||
'name' => $this->randomName(),
|
||||
);
|
||||
$vocab_data_not_translated = array(
|
||||
'name' => $this->randomName(),
|
||||
'machine_name' => 'test_vocab3',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
|
||||
// Configure taxonomy and create vocab + term.
|
||||
$this->drupalPost('admin/structure/taxonomy/add', $vocab_data, t('Save'));
|
||||
$this->drupalGet('admin/structure/taxonomy');
|
||||
$this->clickLink(t('add terms'));
|
||||
$this->drupalPost(NULL, $term_data, t('Save'));
|
||||
$this->drupalPost('admin/structure/taxonomy/add', $vocab_data_not_translated, t('Save'));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_taxonomy_vocabulary');
|
||||
$this->assertText($vocab_data['name']);
|
||||
|
||||
// Request translation via i18n source tab
|
||||
$this->drupalPost(NULL, array('items[taxonomy:vocabulary:1]' => 1), t('Request translation'));
|
||||
// Test for the job checkout url.
|
||||
$this->assertTrue(strpos($this->getUrl(), 'admin/tmgmt/jobs') !== FALSE);
|
||||
entity_get_controller('tmgmt_job')->resetCache();
|
||||
$jobs = entity_load('tmgmt_job', FALSE);
|
||||
/** @var TMGMTJob $job */
|
||||
$job = array_pop($jobs);
|
||||
$this->assertFieldByName('label', $job->label());
|
||||
|
||||
// Request translation via translate tab of i18n.
|
||||
$this->drupalPost('admin/structure/taxonomy/test_vocab/translate', array('languages[taxonomy:vocabulary:1:de]' => 1), t('Request translation'));
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Verify that the job item status is shown.
|
||||
$this->assertText(t('Needs review'));
|
||||
$this->clickLink(t('review'));
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
$this->assertText(t('The translation for @label has been accepted.', array('@label' => $job->label())));
|
||||
|
||||
// Test the missing translation filter.
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_taxonomy_vocabulary');
|
||||
// Check that the source language has been removed from the target language
|
||||
// select box.
|
||||
$elements = $this->xpath('//select[@name=:name]//option[@value=:option]', array(':name' => 'search[target_language]', ':option' => i18n_string_source_language()));
|
||||
$this->assertTrue(empty($elements));
|
||||
$edit = array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated',
|
||||
);
|
||||
$this->drupalPost('admin/tmgmt/sources/i18n_string_taxonomy_vocabulary', $edit, t('Search'));
|
||||
// The vocabulary name is translated to "de" therefore it must not show up
|
||||
// in the list.
|
||||
$this->assertNoText($vocab_data['name']);
|
||||
$this->assertText($vocab_data_not_translated['name']);
|
||||
|
||||
$edit = array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
$this->assertNoText($vocab_data['name']);
|
||||
$this->assertText($vocab_data_not_translated['name']);
|
||||
|
||||
// Update the string status to I18N_STRING_STATUS_UPDATE.
|
||||
$lid = db_select('locales_source', 's')->fields('s', array('lid'))->condition('source', $vocab_data['name'])->execute()->fetchField();
|
||||
db_update('locales_target')->fields(array('i18n_status' => I18N_STRING_STATUS_UPDATE))->condition('lid', $lid)->execute();
|
||||
|
||||
$edit = array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'outdated',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
$this->assertText($vocab_data['name']);
|
||||
$this->assertNoText($vocab_data_not_translated['name']);
|
||||
|
||||
$edit = array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated_or_outdated',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
$this->assertText($vocab_data['name']);
|
||||
$this->assertText($vocab_data_not_translated['name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translation of blocks through the user interface.
|
||||
*/
|
||||
function testI18nStringPluginUIBlock() {
|
||||
|
||||
$this->loginAsAdmin(array('administer blocks', 'translate interface', 'translate user-defined strings'));
|
||||
|
||||
// Make some blocks translatable.
|
||||
$navigation_edit = array(
|
||||
'title' => $this->randomName(),
|
||||
'i18n_mode' => 1,
|
||||
);
|
||||
$this->drupalPost('admin/structure/block/manage/system/navigation/configure', $navigation_edit, t('Save block'));
|
||||
$powered_edit = array(
|
||||
'title' => $this->randomName(),
|
||||
'i18n_mode' => 1,
|
||||
);
|
||||
$this->drupalPost('admin/structure/block/manage/system/powered-by/configure', $powered_edit, t('Save block'));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_block');
|
||||
$this->assertText($navigation_edit['title']);
|
||||
$this->assertText($powered_edit['title']);
|
||||
|
||||
// Request translation via i18n source tab.
|
||||
$edit = array(
|
||||
'items[blocks:system:powered-by]' => 1,
|
||||
'items[blocks:system:navigation]' => 1,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
$this->assertText($navigation_edit['title']);
|
||||
$this->assertText($powered_edit['title']);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
$this->assertRaw(t('Active job item: Needs review'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translation of fields through the user interface.
|
||||
*/
|
||||
function testI18nStringPluginUIField() {
|
||||
$this->loginAsAdmin(array('translate interface', 'translate user-defined strings'));
|
||||
$type = $this->drupalCreateContentType(array('type' => $type = $this->randomName()));
|
||||
|
||||
// Create a field.
|
||||
$field = array(
|
||||
'field_name' => 'list_test',
|
||||
'type' => 'list_text',
|
||||
);
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$field['settings']['allowed_values'][$this->randomName()] = $this->randomString();
|
||||
}
|
||||
field_create_field($field);
|
||||
|
||||
// Create an instance of the previously created field.
|
||||
$instance = array(
|
||||
'field_name' => 'list_test',
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->type,
|
||||
'label' => $this->randomName(10),
|
||||
'description' => $this->randomString(30),
|
||||
);
|
||||
field_create_instance($instance);
|
||||
|
||||
// The body field doesn't have anything that can be translated on the field
|
||||
// level, so it shouldn't show up in the field overview.
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_field');
|
||||
$this->assertNoText(t('Body'));
|
||||
// @todo: Label doesn't work here?
|
||||
$this->assertText('field:list_test:#allowed_values');
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_field_instance');
|
||||
$this->assertUniqueText(t('Body'));
|
||||
$this->assertUniqueText($instance['label']);
|
||||
|
||||
// Request translation.
|
||||
$edit = array(
|
||||
'items[field:body:' . $type->type . ']' => 1,
|
||||
'items[field:list_test:' . $type->type . ']' => 1,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
$this->assertText(t('Body'));
|
||||
$this->assertText($instance['label']);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
$this->assertRaw(t('Active job item: Needs review'));
|
||||
|
||||
// Review the first item.
|
||||
$this->clickLink(t('reviewed'));
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
|
||||
// The overview should now have a translated field and a pending job item.
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_field_instance');
|
||||
$this->assertRaw(t('Translation up to date'));
|
||||
$this->assertRaw(t('Active job item: Needs review'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the i18n specific cart functionality.
|
||||
*/
|
||||
function testCart() {
|
||||
$vocabulary1 = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomName(),
|
||||
'description' => 'description_' . $this->randomName(),
|
||||
'machine_name' => 'test_vocab1',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
));
|
||||
taxonomy_vocabulary_save($vocabulary1);
|
||||
$string1 = "taxonomy:vocabulary:" . $vocabulary1->vid;
|
||||
|
||||
$vocabulary2 = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomName(),
|
||||
'description' => 'description_' . $this->randomName(),
|
||||
'machine_name' => 'test_vocab2',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
));
|
||||
taxonomy_vocabulary_save($vocabulary2);
|
||||
$string2 = "taxonomy:vocabulary:" . $vocabulary2->vid;
|
||||
|
||||
$vocabulary3 = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomName(),
|
||||
'description' => 'description_' . $this->randomName(),
|
||||
'machine_name' => 'test_vocab3',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
));
|
||||
taxonomy_vocabulary_save($vocabulary3);
|
||||
|
||||
$this->loginAsAdmin(array_merge($this->translator_permissions, array('translate interface', 'translate user-defined strings')));
|
||||
|
||||
// Test source overview.
|
||||
$this->drupalPost('admin/tmgmt/sources/i18n_string_taxonomy_vocabulary', array(
|
||||
'items[' . $string1 . ']' => TRUE,
|
||||
'items[' . $string2 . ']' => TRUE,
|
||||
), t('Add to cart'));
|
||||
$this->drupalGet('admin/tmgmt/cart');
|
||||
$this->assertText($vocabulary1->name);
|
||||
$this->assertText($vocabulary2->name);
|
||||
|
||||
// Test translate tab.
|
||||
$this->drupalGet('admin/structure/taxonomy/test_vocab3/translate');
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a>.',
|
||||
array('@count' => 2, '@url' => url('admin/tmgmt/cart'))));
|
||||
|
||||
$this->drupalPost(NULL, array(), t('Add to cart'));
|
||||
$this->assertRaw(t('@count content source was added into the <a href="@url">cart</a>.', array('@count' => 1, '@url' => url('admin/tmgmt/cart'))));
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a> including the current item.',
|
||||
array('@count' => 3, '@url' => url('admin/tmgmt/cart'))));
|
||||
}
|
||||
}
|
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the I18nString source controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class TMGMTI18nStringDefaultSourceUIController
|
||||
*
|
||||
* UI Controller fo i18n strings translation jobs.
|
||||
*/
|
||||
class TMGMTI18nStringDefaultSourceUIController extends TMGMTDefaultSourceUIController {
|
||||
/**
|
||||
* Gets overview form header.
|
||||
*
|
||||
* @return array
|
||||
* Header array definition as expected by theme_tablesort().
|
||||
*/
|
||||
public function overviewFormHeader() {
|
||||
$languages = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$langcode = str_replace('-', '', $langcode);
|
||||
$languages['langcode-' . $langcode] = array(
|
||||
'data' => check_plain($language->name),
|
||||
);
|
||||
}
|
||||
|
||||
$header = array(
|
||||
'title' => array('data' => t('Label (in source language)')),
|
||||
'type' => array('data' => t('Type')),
|
||||
) + $languages;
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewForm($form, &$form_state, $type) {
|
||||
$form += $this->overviewSearchFormPart($form, $form_state, $type);
|
||||
|
||||
$form['items'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $this->overviewFormHeader($type),
|
||||
'#empty' => t('No strings matching given criteria have been found.')
|
||||
);
|
||||
|
||||
$search_data = $this->getSearchFormSubmittedParams();
|
||||
|
||||
$i18n_strings = tmgmt_i18n_string_get_strings($type, $search_data['label'], $search_data['target_language'], $search_data['target_status']);
|
||||
|
||||
foreach ($this->getTranslationData($i18n_strings, $form_state['item_type']) as $id => $data) {
|
||||
$form['items']['#options'][$id] = $this->overviewRow($type, $data);
|
||||
}
|
||||
|
||||
$form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create translation data list for the sources page list.
|
||||
*
|
||||
* @param array $i18n_strings
|
||||
* Result of the search query returned by tmgmt_i18n_string_get_strings().
|
||||
* @param string $type
|
||||
* I18n object type.
|
||||
*
|
||||
* @return array
|
||||
* Structured array with translation data.
|
||||
*/
|
||||
protected function getTranslationData($i18n_strings, $type) {
|
||||
$objects = array();
|
||||
$source_language = variable_get_value('i18n_string_source_language');
|
||||
|
||||
foreach ($i18n_strings as $i18n_string) {
|
||||
$wrapper = tmgmt_i18n_string_get_wrapper($type, $i18n_string);
|
||||
|
||||
if ($wrapper instanceof i18n_string_object_wrapper) {
|
||||
$id = $i18n_string->job_item_id;
|
||||
|
||||
// Get existing translations and current job items for the entity
|
||||
// to determine translation statuses
|
||||
$current_job_items = tmgmt_job_item_load_latest('i18n_string', $wrapper->get_type(), $id, $source_language);
|
||||
|
||||
$objects[$id] = array(
|
||||
'id' => $id,
|
||||
'object' => $wrapper->get_strings(array('empty' => TRUE)),
|
||||
'wrapper' => $wrapper,
|
||||
);
|
||||
// Load entity translation specific data.
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$langcode = str_replace('-', '', $langcode);
|
||||
|
||||
$translation_status = 'current';
|
||||
|
||||
if ($langcode == $source_language) {
|
||||
$translation_status = 'original';
|
||||
}
|
||||
elseif ($i18n_string->{'lang_' . $langcode} === NULL) {
|
||||
$translation_status = 'missing';
|
||||
}
|
||||
|
||||
$objects[$id]['current_job_items'][$langcode] = isset($current_job_items[$langcode]) ? $current_job_items[$langcode] : NULL;
|
||||
$objects[$id]['translation_statuses'][$langcode] = $translation_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds search form for entity sources overview.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*
|
||||
* @return array
|
||||
* Drupal form array.
|
||||
*/
|
||||
public function overviewSearchFormPart($form, &$form_state, $type) {
|
||||
|
||||
$options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$default_values = $this->getSearchFormSubmittedParams();
|
||||
|
||||
$form['search_wrapper'] = array(
|
||||
'#prefix' => '<div class="tmgmt-sources-wrapper tmgmt-i18n_string-sources-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -15,
|
||||
);
|
||||
$form['search_wrapper']['search'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
$form['search_wrapper']['search']['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Label in source language'),
|
||||
'#default_value' => isset($default_values['label']) ? $default_values['label'] : NULL,
|
||||
);
|
||||
|
||||
// Unset the source language as it should not be listed among target
|
||||
// languages.
|
||||
unset($options[i18n_string_source_language()]);
|
||||
|
||||
$form['search_wrapper']['search']['target_language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target language'),
|
||||
'#options' => $options,
|
||||
'#empty_option' => t('Any'),
|
||||
'#default_value' => isset($default_values['target_language']) ? $default_values['target_language'] : NULL,
|
||||
);
|
||||
$form['search_wrapper']['search']['target_status'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target status'),
|
||||
'#options' => array(
|
||||
'untranslated_or_outdated' => t('Untranslated or outdated'),
|
||||
'untranslated' => t('Untranslated'),
|
||||
'outdated' => t('Outdated'),
|
||||
),
|
||||
'#default_value' => isset($default_values['target_status']) ? $default_values['target_status'] : NULL,
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
':input[name="search[target_language]"]' => array('value' => ''),
|
||||
),
|
||||
),
|
||||
);
|
||||
$form['search_wrapper']['search_submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Search'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets submitted search params.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSearchFormSubmittedParams() {
|
||||
$params = array(
|
||||
'label' => NULL,
|
||||
'target_language' => NULL,
|
||||
'target_status' => NULL,
|
||||
);
|
||||
|
||||
if (isset($_GET['label'])) {
|
||||
$params['label'] = $_GET['label'];
|
||||
}
|
||||
if (isset($_GET['target_language'])) {
|
||||
$params['target_language'] = $_GET['target_language'];
|
||||
}
|
||||
if (isset($_GET['target_status'])) {
|
||||
$params['target_status'] = $_GET['target_status'];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a table row for overview form.
|
||||
*
|
||||
* @param string $type
|
||||
* i18n type.
|
||||
* @param array $data
|
||||
* Data needed to build the list row.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function overviewRow($type, $data) {
|
||||
// Set the default item key, assume it's the first.
|
||||
$item_title = reset($data['object']);
|
||||
|
||||
$type_label = i18n_object_info($type, 'title');
|
||||
|
||||
$row = array(
|
||||
'id' => $data['id'],
|
||||
'title' => $item_title->get_string() ? t('@title (@id)', array('@title' => $item_title->get_string(), '@id' => $data['id'])) : $data['id'],
|
||||
'type' => empty($type_label) ? t('Unknown') : $type_label,
|
||||
);
|
||||
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$langcode = str_replace('-', '', $langcode);
|
||||
$row['langcode-' . $langcode] = theme('tmgmt_ui_translation_language_status_single', array(
|
||||
'translation_status' => $data['translation_statuses'][$langcode],
|
||||
'job_item' => isset($data['current_job_items'][$langcode]) ? $data['current_job_items'][$langcode] : NULL,
|
||||
));
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewFormSubmit($form, &$form_state, $type) {
|
||||
|
||||
// Handle search redirect.
|
||||
$this->overviewSearchFormRedirect($form, $form_state, $type);
|
||||
$items = array_filter($form_state['values']['items']);
|
||||
$type = $form_state['item_type'];
|
||||
|
||||
$source_lang = variable_get_value('i18n_string_source_language');
|
||||
|
||||
// Create only single job for all items as the source language is just
|
||||
// the same for all.
|
||||
$job = tmgmt_job_create($source_lang, NULL, $GLOBALS['user']->uid);
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($items as $item) {
|
||||
$job->addItem('i18n_string', $type, $item);
|
||||
}
|
||||
|
||||
$form_state['redirect'] = array('admin/tmgmt/jobs/' . $job->tjid,
|
||||
array('query' => array('destination' => current_path())));
|
||||
drupal_set_message(t('One job needs to be checked out.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs redirect with search params appended to the uri.
|
||||
*
|
||||
* In case of triggering element is edit-search-submit it redirects to
|
||||
* current location with added query string containing submitted search form
|
||||
* values.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*/
|
||||
public function overviewSearchFormRedirect($form, &$form_state, $type) {
|
||||
if ($form_state['triggering_element']['#id'] == 'edit-search-submit') {
|
||||
|
||||
$query = array();
|
||||
|
||||
foreach ($form_state['values']['search'] as $key => $value) {
|
||||
$query[$key] = $value;
|
||||
}
|
||||
|
||||
drupal_goto($_GET['q'], array('query' => $query));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Drupal 7\\n"
|
||||
"MIME-Version: 1.0\\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\\n"
|
||||
"Content-Transfer-Encoding: 8bit\\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
|
||||
|
||||
msgid "Hello World"
|
||||
msgstr "Hallo Welt"
|
||||
|
||||
msgid "Example"
|
||||
msgstr "Beispiel"
|
@@ -0,0 +1,17 @@
|
||||
name = Locales Source
|
||||
description = Locales source plugin for the Translation Management system.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
dependencies[] = tmgmt
|
||||
dependencies[] = locale
|
||||
files[] = tmgmt_locale.plugin.inc
|
||||
files[] = tmgmt_locale.test
|
||||
files[] = tmgmt_locale.ui.inc
|
||||
files[] = tmgmt_locale.ui.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Installation hooks for tmgmt_locale module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update existing {locales_target}.l10n_status if any.
|
||||
*/
|
||||
function tmgmt_locale_update_7000() {
|
||||
if (module_exists('l10n_update')) {
|
||||
module_load_include('inc', 'l10n_update');
|
||||
$query = db_select('tmgmt_job_item', 'ji')
|
||||
->condition('ji.plugin', 'locale')
|
||||
->condition('ji.state', TMGMT_JOB_ITEM_STATE_ACCEPTED);
|
||||
$query->innerJoin('tmgmt_job', 'j', 'j.tjid = ji.tjid');
|
||||
$query->addField('ji', 'item_id', 'lid');
|
||||
$query->addField('j', 'target_language', 'language');
|
||||
foreach ($query->execute() as $row) {
|
||||
db_update('locales_target')
|
||||
->condition('lid', $row->lid)
|
||||
->condition('language', $row->language)
|
||||
->fields(array('l10n_status' => L10N_UPDATE_STRING_CUSTOM))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Source plugin for the Translation Management system that handles locale strings.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_plugin_info().
|
||||
*
|
||||
* @see TMGMTLocaleSourcePluginController
|
||||
*/
|
||||
function tmgmt_locale_tmgmt_source_plugin_info() {
|
||||
$info['locale'] = array(
|
||||
'label' => t('Locale source'),
|
||||
'description' => t('Source handler for locale strings.'),
|
||||
'plugin controller class' => 'TMGMTLocaleSourcePluginController',
|
||||
'ui controller class' => 'TMGMTLocaleSourceUIController',
|
||||
'item types' => array(
|
||||
'default' => t('Locale'),
|
||||
),
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the locale source controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translation plugin controller for locale strings.
|
||||
*/
|
||||
class TMGMTLocaleSourcePluginController extends TMGMTDefaultSourcePluginController {
|
||||
|
||||
/**
|
||||
* Updates translation associated to a specific locale source.
|
||||
*
|
||||
* @param string $lid
|
||||
* The Locale ID.
|
||||
* @param string $target_language
|
||||
* Target language to update translation.
|
||||
* @param string $translation
|
||||
* Translation value.
|
||||
*
|
||||
* @return bool
|
||||
* Success or not updating the locale translation.
|
||||
*/
|
||||
protected function updateTranslation($lid, $target_language, $translation) {
|
||||
|
||||
$languages = locale_language_list('name', TRUE);
|
||||
if (!$lid || !array_key_exists($target_language, $languages) || !$translation) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$exists = db_query("SELECT COUNT(lid) FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $target_language))
|
||||
->fetchField();
|
||||
|
||||
$fields = array(
|
||||
'translation' => $translation,
|
||||
);
|
||||
if (module_exists('l10n_update')) {
|
||||
module_load_include('inc', 'l10n_update');
|
||||
$fields += array(
|
||||
'l10n_status' => L10N_UPDATE_STRING_CUSTOM,
|
||||
);
|
||||
}
|
||||
|
||||
// @todo Only singular strings are managed here, we should take care of
|
||||
// plural information of processed string.
|
||||
if (!$exists) {
|
||||
$fields += array(
|
||||
'lid' => $lid,
|
||||
'language' => $target_language,
|
||||
);
|
||||
db_insert('locales_target')
|
||||
->fields($fields)
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
db_update('locales_target')
|
||||
->fields($fields)
|
||||
->condition('lid', $lid)
|
||||
->condition('language', $target_language)
|
||||
->execute();
|
||||
}
|
||||
// Clear locale caches.
|
||||
_locale_invalidate_js($target_language);
|
||||
cache_clear_all('locale:' . $target_language, 'cache');
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to obtain a locale object for given job item.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
*
|
||||
* @return locale object
|
||||
*/
|
||||
protected function getLocaleObject(TMGMTJobItem $job_item) {
|
||||
$locale_lid = $job_item->item_id;
|
||||
|
||||
// Check existence of assigned lid.
|
||||
$exists = db_query("SELECT COUNT(lid) FROM {locales_source} WHERE lid = :lid", array(':lid' => $locale_lid))->fetchField();
|
||||
if (!$exists) {
|
||||
throw new TMGMTException(t('Unable to load locale with id %id', array('%id' => $job_item->item_id)));
|
||||
}
|
||||
|
||||
// This is necessary as the method is also used in the getLabel() callback
|
||||
// and for that case the job is not available in the cart.
|
||||
if (!empty($job_item->tjid)) {
|
||||
$source_language = $job_item->getJob()->source_language;
|
||||
}
|
||||
else {
|
||||
$source_language = $job_item->getSourceLangCode();
|
||||
}
|
||||
|
||||
if ($source_language == 'en') {
|
||||
$query = db_select('locales_source', 'ls');
|
||||
$query
|
||||
->fields('ls')
|
||||
->condition('ls.lid', $locale_lid);
|
||||
$locale_object = $query
|
||||
->execute()
|
||||
->fetchObject();
|
||||
|
||||
$locale_object->language = 'en';
|
||||
|
||||
if (empty($locale_object)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$locale_object->origin = 'source';
|
||||
}
|
||||
else {
|
||||
$query = db_select('locales_target', 'lt');
|
||||
$query->join('locales_source', 'ls', 'ls.lid = lt.lid');
|
||||
$query
|
||||
->fields('lt')
|
||||
->fields('ls')
|
||||
->condition('lt.lid', $locale_lid)
|
||||
->condition('lt.language', $source_language);
|
||||
$locale_object = $query
|
||||
->execute()
|
||||
->fetchObject();
|
||||
|
||||
if (empty($locale_object)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$locale_object->origin = 'target';
|
||||
}
|
||||
|
||||
return $locale_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
if ($locale_object = $this->getLocaleObject($job_item)) {
|
||||
if ($locale_object->origin == 'source') {
|
||||
$label = $locale_object->source;
|
||||
}
|
||||
else {
|
||||
$label = $locale_object->translation;
|
||||
}
|
||||
return truncate_utf8(strip_tags($label), 30, FALSE, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [@inheritdoc}
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item) {
|
||||
return $this->getItemTypeLabel($job_item->item_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
$locale_object = $this->getLocaleObject($job_item);
|
||||
if (empty($locale_object)) {
|
||||
$languages = language_list();
|
||||
throw new TMGMTException(t('Unable to load %language translation for the locale %id',
|
||||
array('%language' => $languages[$job_item->getJob()->source_language]->name, '%id' => $job_item->item_id)));
|
||||
}
|
||||
|
||||
if ($locale_object->origin == 'source') {
|
||||
$text = $locale_object->source;
|
||||
}
|
||||
else {
|
||||
$text = $locale_object->translation;
|
||||
}
|
||||
|
||||
// Identify placeholders that need to be escaped. Assume that placeholders
|
||||
// consist of alphanumeric characters and _,- only and are delimited by
|
||||
// non-alphanumeric characters. There are cases that don't match, for
|
||||
// example appended SI units like "@valuems", there only @value is the
|
||||
// actual placeholder.
|
||||
$escape = array();
|
||||
if (preg_match_all('/([@!%][a-zA-Z0-9_-]+)/', $text, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
foreach ($matches[0] as $match) {
|
||||
$escape[$match[1]]['string'] = $match[0];
|
||||
}
|
||||
}
|
||||
$structure['singular'] = array(
|
||||
'#label' => t('Singular'),
|
||||
'#text' => (string) $text,
|
||||
'#translate' => TRUE,
|
||||
'#escape' => $escape,
|
||||
);
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item) {
|
||||
$job = tmgmt_job_load($job_item->tjid);
|
||||
$data = $job_item->getData();
|
||||
if (isset($data['singular'])) {
|
||||
$translation = $data['singular']['#translation']['#text'];
|
||||
// Update the locale string in the system.
|
||||
// @todo: Send error message to user if update fails.
|
||||
if ($this->updateTranslation($job_item->item_id, $job->target_language, $translation)) {
|
||||
$job_item->accepted();
|
||||
}
|
||||
}
|
||||
|
||||
// @todo: Temporary backwards compability with existing jobs, remove in next
|
||||
// release.
|
||||
if (isset($data[$job_item->item_id])) {
|
||||
$translation = $data[$job_item->item_id]['#translation']['#text'];
|
||||
// Update the locale string in the system.
|
||||
// @todo: Send error message to user if update fails.
|
||||
if ($this->updateTranslation($job_item->item_id, $job->target_language, $translation)) {
|
||||
$job_item->accepted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item) {
|
||||
// For the locale source English is always the source language.
|
||||
return 'en';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
$query = db_select('locales_target', 'lt');
|
||||
$query->fields('lt', array('language'));
|
||||
$query->condition('lt.lid', $job_item->item_id);
|
||||
|
||||
$existing_lang_codes = array('en');
|
||||
foreach ($query->execute() as $language) {
|
||||
$existing_lang_codes[] = $language->language;
|
||||
}
|
||||
|
||||
return $existing_lang_codes;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Locale Source tests.
|
||||
*/
|
||||
class TMGMTLocaleSourceTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Locale Source tests',
|
||||
'description' => 'Exporting source data from locale and saving translations back',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_locale'));
|
||||
$this->langcode = 'de';
|
||||
$this->context = 'default';
|
||||
$file = new stdClass();
|
||||
$file->uri = drupal_realpath(drupal_get_path('module', 'tmgmt_locale') . '/tests/test.xx.po');
|
||||
$this->pofile = file_save($file);
|
||||
$this->setEnvironment($this->langcode);
|
||||
$this->setEnvironment('es');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translation of a locale singular term.
|
||||
*/
|
||||
function testSingularTerm() {
|
||||
// Load PO file to create a locale structure in the database.
|
||||
_locale_import_po($this->pofile, $this->langcode, LOCALE_IMPORT_OVERWRITE, $this->context);
|
||||
|
||||
// Obtain one locale string with translation.
|
||||
$locale_object = db_query('SELECT * FROM {locales_source} WHERE source = :source LIMIT 1', array(':source' => 'Hello World'))->fetchObject();
|
||||
$source_text = $locale_object->source;
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item1 = $job->addItem('locale', 'default', $locale_object->lid);
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $locale_object->lid, 'Locale Strings object correctly saved');
|
||||
$this->assertEqual('Locale', $item1->getSourceType());
|
||||
$this->assertEqual('Hello World', $item1->getSourceLabel());
|
||||
$job->requestTranslation();
|
||||
|
||||
foreach ($job->getItems() as $item) {
|
||||
/* @var $item TMGMTJobItem */
|
||||
$item->acceptTranslation();
|
||||
$this->assertTrue($item->isAccepted());
|
||||
// The source is now available in en and de.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en', 'de'));
|
||||
}
|
||||
|
||||
// Check string translation.
|
||||
$expected_translation = $job->target_language . '_' . $source_text;
|
||||
$this->assertTranslation($locale_object->lid, 'de', $expected_translation);
|
||||
|
||||
// Translate the german translation to spanish.
|
||||
$target_langcode = 'es';
|
||||
$job = $this->createJob('de', $target_langcode);
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item1 = $job->addItem('locale', 'default', $locale_object->lid);
|
||||
$this->assertEqual('Locale', $item1->getSourceType());
|
||||
$this->assertEqual($expected_translation, $item1->getSourceLabel());
|
||||
$job->requestTranslation();
|
||||
|
||||
foreach ($job->getItems() as $item) {
|
||||
/* @var $item TMGMTJobItem */
|
||||
$item->acceptTranslation();
|
||||
$this->assertTrue($item->isAccepted());
|
||||
|
||||
// The source should be now available for en, de and es languages.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en', 'de', 'es'));
|
||||
}
|
||||
|
||||
// Check string translation.
|
||||
$this->assertTranslation($locale_object->lid, $target_langcode, $job->target_language . '_' . $expected_translation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the source is able to pull content in requested language.
|
||||
*/
|
||||
function testRequestDataForSpecificLanguage() {
|
||||
$this->setEnvironment('cs');
|
||||
|
||||
_locale_import_po($this->pofile, $this->langcode, LOCALE_IMPORT_OVERWRITE, $this->context);
|
||||
$locale_object = db_query('SELECT * FROM {locales_source} WHERE source = :source LIMIT 1', array(':source' => 'Hello World'))->fetchObject();
|
||||
|
||||
$plugin = new TMGMTLocaleSourcePluginController('locale', 'locale');
|
||||
$reflection_plugin = new ReflectionClass('TMGMTLocaleSourcePluginController');
|
||||
$updateTranslation = $reflection_plugin->getMethod('updateTranslation');
|
||||
$updateTranslation->setAccessible(TRUE);
|
||||
|
||||
$updateTranslation->invoke($plugin, $locale_object->lid, 'de', 'de translation');
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob('de', 'cs');
|
||||
$job->save();
|
||||
$job->addItem('locale', 'default', $locale_object->lid);
|
||||
|
||||
$data = $job->getData();
|
||||
$this->assertEqual($data[1]['singular']['#text'], 'de translation');
|
||||
|
||||
// Create new job item with a source language for which the translation
|
||||
// does not exit.
|
||||
$job = $this->createJob('es', 'cs');
|
||||
$job->save();
|
||||
try {
|
||||
$job->addItem('locale', 'default', $locale_object->lid);
|
||||
$this->fail('The job item should not be added as there is no translation for language "es"');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$languages = language_list();
|
||||
$this->assertEqual(t('Unable to load %language translation for the locale %id',
|
||||
array('%language' => $languages['es']->name, '%id' => $locale_object->lid)), $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that strings that need escaping are correctly identified.
|
||||
*/
|
||||
function testEscaping() {
|
||||
$lid = db_insert('locales_source')
|
||||
->fields(array(
|
||||
'source' => '@place-holders need %to be !esc_aped.',
|
||||
'textgroup' => 'default',
|
||||
'context' => '',
|
||||
))
|
||||
->execute();
|
||||
$job = $this->createJob('en', 'de');
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item = $job->addItem('locale', 'default', $lid);
|
||||
$data = $item->getData();
|
||||
$expected_escape = array(
|
||||
0 => array('string' => '@place-holders'),
|
||||
20 => array('string' => '%to'),
|
||||
27 => array('string' => '!esc_aped'),
|
||||
);
|
||||
$this->assertEqual($data['singular']['#escape'], $expected_escape);
|
||||
|
||||
// Invalid patterns that should be ignored.
|
||||
$lid = db_insert('locales_source')
|
||||
->fields(array(
|
||||
'source' => '@ % ! example',
|
||||
'textgroup' => 'default',
|
||||
'context' => '',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$item = $job->addItem('locale', 'default', $lid);
|
||||
$data = $item->getData();
|
||||
$this->assertTrue(empty($data[$lid]['#escape']));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that system behaves correctly with an non-existing locales.
|
||||
*/
|
||||
function testInexistantSource() {
|
||||
// Create inexistant locale object.
|
||||
$locale_object = new stdClass();
|
||||
$locale_object->lid = 0;
|
||||
|
||||
// Create the job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
// Create the job item.
|
||||
try {
|
||||
$job->addItem('locale', 'default', $locale_object->lid);
|
||||
$this->fail('Job item add with an inexistant locale.');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$this->pass('Exception thrown when trying to translate non-existing locale string');
|
||||
}
|
||||
|
||||
// Try to translate a source string without translation from german to
|
||||
// spanish.
|
||||
$lid = db_insert('locales_source')
|
||||
->fields(array(
|
||||
'source' => 'No translation',
|
||||
'textgroup' => 'default',
|
||||
'context' => '',
|
||||
))
|
||||
->execute();
|
||||
$job = $this->createJob('de', 'fr');
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
try {
|
||||
$job->addItem('locale', 'default', $lid);
|
||||
$this->fail('Job item add with an non-existing locale did not fail.');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$this->pass('Job item add with an non-existing locale did fail.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts a locale translation.
|
||||
*
|
||||
* @param int $lid
|
||||
* The locale source id.
|
||||
* @param string $target_langcode
|
||||
* The target language code.
|
||||
* @param string $expected_translation
|
||||
* The expected translation.
|
||||
*/
|
||||
public function assertTranslation($lid, $target_langcode, $expected_translation) {
|
||||
$actual_translation = db_query('SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language', array(
|
||||
':lid' => $lid,
|
||||
':language' => $target_langcode
|
||||
))->fetchField();
|
||||
$this->assertEqual($actual_translation, $expected_translation);
|
||||
}
|
||||
}
|
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the I18nString source controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class TMGMTI18nStringDefaultSourceUIController
|
||||
*
|
||||
* UI Controller fo i18n strings translation jobs.
|
||||
*/
|
||||
class TMGMTLocaleSourceUIController extends TMGMTDefaultSourceUIController {
|
||||
|
||||
/**
|
||||
* Gets locale strings.
|
||||
*
|
||||
* @param string $textgroup
|
||||
* The locale textgroup.
|
||||
* @param string $search_label
|
||||
* Label to search for.
|
||||
* @param string $missing_target_language
|
||||
* Missing translation language.
|
||||
*
|
||||
* @return array
|
||||
* List of i18n strings data.
|
||||
*/
|
||||
function getStrings($textgroup, $search_label = NULL, $missing_target_language = NULL) {
|
||||
$languages = drupal_map_assoc(array_keys(language_list()));
|
||||
$select = db_select('locales_source', 'ls')
|
||||
->fields('ls', array('lid', 'source'));
|
||||
|
||||
$select->addTag('tmgmt_sources_search');
|
||||
$select->addMetaData('plugin', 'locale');
|
||||
$select->addMetaData('type', $textgroup);
|
||||
|
||||
$select->condition('ls.textgroup', $textgroup);
|
||||
if (!empty($search_label)) {
|
||||
$select->condition('ls.source', "%$search_label%", 'LIKE');
|
||||
}
|
||||
if (!empty($missing_target_language) && in_array($missing_target_language, $languages)) {
|
||||
$select->isNull("lt_$missing_target_language.language");
|
||||
}
|
||||
|
||||
// Join locale targets for each language.
|
||||
// We want all joined fields to be named as langcodes, but langcodes could
|
||||
// contain hyphens in their names, which is not allowed by the most database
|
||||
// engines. So we create a langcode-to-filed_alias map, and rename fields
|
||||
// later.
|
||||
$langcode_to_filed_alias_map = array();
|
||||
foreach ($languages as $langcode) {
|
||||
$table_alias = $select->leftJoin('locales_target', db_escape_field("lt_$langcode"), "ls.lid = %alias.lid AND %alias.language = '$langcode'");
|
||||
$langcode_to_filed_alias_map[$langcode] = $select->addField($table_alias, 'language');
|
||||
}
|
||||
|
||||
$select = $select->extend('PagerDefault')->limit(variable_get('tmgmt_source_list_limit', 20));
|
||||
$rows = $select->execute()->fetchAll();
|
||||
foreach ($rows as $row) {
|
||||
foreach ($langcode_to_filed_alias_map as $langcode => $field_alias) {
|
||||
$row->{$langcode} = $row->{$field_alias};
|
||||
unset($row->{$field_alias});
|
||||
}
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets overview form header.
|
||||
*
|
||||
* @return array
|
||||
* Header array definition as expected by theme_tablesort().
|
||||
*/
|
||||
public function overviewFormHeader() {
|
||||
$languages = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$languages['langcode-' . $langcode] = array(
|
||||
'data' => check_plain($language->name),
|
||||
);
|
||||
}
|
||||
|
||||
$header = array(
|
||||
'source' => array('data' => t('Source text')),
|
||||
) + $languages;
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements TMGMTSourceUIControllerInterface::overviewForm().
|
||||
*/
|
||||
public function overviewForm($form, &$form_state, $type) {
|
||||
$form += $this->overviewSearchFormPart($form, $form_state, $type);
|
||||
|
||||
$form['items'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $this->overviewFormHeader($type),
|
||||
'#empty' => t('No strings matching given criteria have been found.')
|
||||
);
|
||||
|
||||
$search_data = $this->getSearchFormSubmittedParams();
|
||||
|
||||
$strings = $this->getStrings($type, $search_data['label'], $search_data['missing_target_language']);
|
||||
|
||||
foreach ($this->getTranslationData($strings, $type) as $id => $data) {
|
||||
$form['items']['#options'][$id] = $this->overviewRow($type, $data);
|
||||
}
|
||||
|
||||
$form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create translation data list for the sources page list.
|
||||
*
|
||||
* @param array $strings
|
||||
* Result of the search query returned by tmgmt_i18n_string_get_strings().
|
||||
* @param string $type
|
||||
* I18n object type.
|
||||
*
|
||||
* @return array
|
||||
* Structured array with translation data.
|
||||
*/
|
||||
protected function getTranslationData($strings, $type) {
|
||||
$objects = array();
|
||||
// Source language of locale strings is always english.
|
||||
$source_language = 'en';
|
||||
|
||||
foreach ($strings as $string) {
|
||||
$id = $string->lid;
|
||||
|
||||
// Get existing translations and current job items for the entity
|
||||
// to determine translation statuses
|
||||
$current_job_items = tmgmt_job_item_load_latest('locale', $type, $id, $source_language);
|
||||
|
||||
$objects[$id] = array(
|
||||
'id' => $id,
|
||||
'object' => $string
|
||||
);
|
||||
// Load entity translation specific data.
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$translation_status = 'current';
|
||||
|
||||
if ($langcode == $source_language) {
|
||||
$translation_status = 'original';
|
||||
}
|
||||
elseif ($string->{$langcode} === NULL) {
|
||||
$translation_status = 'missing';
|
||||
}
|
||||
|
||||
$objects[$id]['current_job_items'][$langcode] = isset($current_job_items[$langcode]) ? $current_job_items[$langcode] : NULL;
|
||||
$objects[$id]['translation_statuses'][$langcode] = $translation_status;
|
||||
}
|
||||
}
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds search form for entity sources overview.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*
|
||||
* @return array
|
||||
* Drupal form array.
|
||||
*/
|
||||
public function overviewSearchFormPart($form, &$form_state, $type) {
|
||||
|
||||
$options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$default_values = $this->getSearchFormSubmittedParams();
|
||||
|
||||
$form['search_wrapper'] = array(
|
||||
'#prefix' => '<div class="tmgmt-sources-wrapper tmgmt-i18n_string-sources-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -15,
|
||||
);
|
||||
$form['search_wrapper']['search'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
$form['search_wrapper']['search']['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Source text'),
|
||||
'#default_value' => isset($default_values['label']) ? $default_values['label'] : NULL,
|
||||
);
|
||||
|
||||
// Unset English as it is the source language for all locale strings.
|
||||
unset($options['en']);
|
||||
|
||||
$form['search_wrapper']['search']['missing_target_language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Not translated to'),
|
||||
'#options' => $options,
|
||||
'#empty_option' => '--',
|
||||
'#default_value' => isset($default_values['missing_target_language']) ? $default_values['missing_target_language'] : NULL,
|
||||
);
|
||||
$form['search_wrapper']['search_submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Search'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets submitted search params.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSearchFormSubmittedParams() {
|
||||
$params = array(
|
||||
'label' => NULL,
|
||||
'missing_target_language' => NULL,
|
||||
);
|
||||
|
||||
if (isset($_GET['label'])) {
|
||||
$params['label'] = $_GET['label'];
|
||||
}
|
||||
if (isset($_GET['missing_target_language'])) {
|
||||
$params['missing_target_language'] = $_GET['missing_target_language'];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a table row for overview form.
|
||||
*
|
||||
* @param string $type
|
||||
* i18n type.
|
||||
* @param array $data
|
||||
* Data needed to build the list row.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function overviewRow($type, $data) {
|
||||
// Set the default item key, assume it's the first.
|
||||
$source = $data['object'];
|
||||
|
||||
$row = array(
|
||||
'id' => $data['id'],
|
||||
'source' => check_plain($source->source),
|
||||
);
|
||||
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$row['langcode-' . $langcode] = theme('tmgmt_ui_translation_language_status_single', array(
|
||||
'translation_status' => $data['translation_statuses'][$langcode],
|
||||
'job_item' => isset($data['current_job_items'][$langcode]) ? $data['current_job_items'][$langcode] : NULL,
|
||||
));
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements TMGMTSourceUIControllerInterface::overviewFormSubmit().
|
||||
*/
|
||||
public function overviewFormSubmit($form, &$form_state, $type) {
|
||||
// Handle search redirect.
|
||||
$this->overviewSearchFormRedirect($form, $form_state, $type);
|
||||
$items = array_filter($form_state['values']['items']);
|
||||
$type = $form_state['item_type'];
|
||||
|
||||
$source_lang = 'en';
|
||||
|
||||
// Create only single job for all items as the source language is just
|
||||
// the same for all.
|
||||
$job = tmgmt_job_create($source_lang, NULL, $GLOBALS['user']->uid);
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($items as $item) {
|
||||
$job->addItem('locale', $type, $item);
|
||||
}
|
||||
|
||||
$form_state['redirect'] = array('admin/tmgmt/jobs/' . $job->tjid,
|
||||
array('query' => array('destination' => current_path())));
|
||||
drupal_set_message(t('One job needs to be checked out.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs redirect with search params appended to the uri.
|
||||
*
|
||||
* In case of triggering element is edit-search-submit it redirects to
|
||||
* current location with added query string containing submitted search form
|
||||
* values.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*/
|
||||
public function overviewSearchFormRedirect($form, &$form_state, $type) {
|
||||
if ($form_state['triggering_element']['#id'] == 'edit-search-submit') {
|
||||
|
||||
$query = array();
|
||||
|
||||
foreach ($form_state['values']['search'] as $key => $value) {
|
||||
$query[$key] = $value;
|
||||
}
|
||||
|
||||
drupal_goto($_GET['q'], array('query' => $query));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Locale Source tests.
|
||||
*/
|
||||
class TMGMTLocaleSourceUiTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Locale Source UI tests',
|
||||
'description' => 'Tests the locale source overview',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_locale', 'tmgmt_ui'));
|
||||
$this->langcode = 'de';
|
||||
$this->context = 'default';
|
||||
$file = new stdClass();
|
||||
$file->uri = drupal_realpath(drupal_get_path('module', 'tmgmt_locale') . '/tests/test.xx.po');
|
||||
$this->pofile = file_save($file);
|
||||
$this->setEnvironment($this->langcode);
|
||||
$this->setEnvironment('gsw-berne');
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function testOverview() {
|
||||
// Load PO file to create a locale structure in the database.
|
||||
_locale_import_po($this->pofile, $this->langcode, LOCALE_IMPORT_OVERWRITE, $this->context);
|
||||
|
||||
$this->loginAsTranslator();
|
||||
$this->drupalGet('admin/tmgmt/sources/locale_default');
|
||||
|
||||
$this->assertText('Hello World');
|
||||
$this->assertText('Example');
|
||||
$rows = $this->xpath('//tbody/tr');
|
||||
foreach ($rows as $row) {
|
||||
if ($row->td[1] == 'Hello World') {
|
||||
$this->assertEqual((string) $row->td[3]->div['title'], t('Translation up to date'));
|
||||
$this->assertEqual((string) $row->td[4]->div['title'], t('Not translated'));
|
||||
}
|
||||
}
|
||||
|
||||
// Filter on the label.
|
||||
$edit = array('search[label]' => 'Hello');
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
|
||||
$this->assertText('Hello World');
|
||||
$this->assertNoText('Example');
|
||||
|
||||
$locale_object = db_query('SELECT * FROM {locales_source} WHERE source = :source LIMIT 1', array(':source' => 'Hello World'))->fetchObject();
|
||||
|
||||
// First add source to the cart to test its functionality.
|
||||
$edit = array(
|
||||
'items[' . $locale_object->lid . ']' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Add to cart'));
|
||||
$this->assertRaw(t('@count content source was added into the <a href="@url">cart</a>.', array('@count' => 1, '@url' => url('admin/tmgmt/cart'))));
|
||||
$edit['target_language[]'] = array('gsw-berne');
|
||||
$this->drupalPost('admin/tmgmt/cart', $edit, t('Request translation'));
|
||||
|
||||
// Assert that the job item is displayed.
|
||||
$this->assertText('Hello World');
|
||||
$this->assertText(t('Locale'));
|
||||
$this->assertText('2');
|
||||
$this->drupalPost(NULL, array('target_language' => 'gsw-berne'), t('Submit to translator'));
|
||||
|
||||
// Test for the translation flag title.
|
||||
$this->drupalGet('admin/tmgmt/sources/locale_default');
|
||||
$this->assertRaw(t('Active job item: Needs review'));
|
||||
|
||||
// Review and accept the job item.
|
||||
$job_items = tmgmt_job_item_load_latest('locale', 'default', $locale_object->lid, 'en');
|
||||
$this->drupalGet('admin/tmgmt/items/' . $job_items['gsw-berne']->tjiid);
|
||||
$this->assertRaw('gsw-berne_Hello World');
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
$this->drupalGet('admin/tmgmt/sources/locale_default');
|
||||
|
||||
$this->assertNoRaw(t('Active job item: Needs review'));
|
||||
$rows = $this->xpath('//tbody/tr');
|
||||
foreach ($rows as $row) {
|
||||
if ($row->td[1] == 'Hello World') {
|
||||
$this->assertEqual((string) $row->td[3]->div['title'], t('Translation up to date'));
|
||||
$this->assertEqual((string) $row->td[4]->div['title'], t('Translation up to date'));
|
||||
}
|
||||
}
|
||||
|
||||
// Test the missing translation filter.
|
||||
$this->drupalGet('admin/tmgmt/sources/locale_default');
|
||||
// Check that the source language (en) has been removed from the target language
|
||||
// select box.
|
||||
$elements = $this->xpath('//select[@name=:name]//option[@value=:option]', array(':name' => 'search[target_language]', ':option' => 'en'));
|
||||
$this->assertTrue(empty($elements));
|
||||
|
||||
// Filter on the "Not translated to".
|
||||
$edit = array('search[missing_target_language]' => 'gsw-berne');
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
// Hello World is translated to "gsw-berne" therefore it must not show up in the
|
||||
// list.
|
||||
$this->assertNoText('Hello World');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the node source plugin for TMGMT.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter the created node translation.
|
||||
*
|
||||
* @param object
|
||||
* $tnode translated node
|
||||
* @param object
|
||||
* $node source node
|
||||
* @param TMGMTJobItem
|
||||
* $job_item
|
||||
*/
|
||||
function hook_tmgmt_before_update_node_translation_alter($tnode, $node, $job_item) {
|
||||
// Always store new translations as a new revision.
|
||||
$tnode->revision = 1;
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
name = Content Source
|
||||
description = Content Translation source plugin for the Translation Management system.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = tmgmt
|
||||
dependencies[] = tmgmt_field
|
||||
dependencies[] = translation
|
||||
|
||||
files[] = tmgmt_node.plugin.inc
|
||||
files[] = tmgmt_node.ui.inc
|
||||
files[] = tmgmt_node.test
|
||||
|
||||
; Views integration and handlers
|
||||
files[] = views/tmgmt_node.views.inc
|
||||
files[] = views/handlers/tmgmt_node_handler_field_translation_language_status.inc
|
||||
files[] = views/handlers/tmgmt_node_handler_field_translation_language_status_single.inc
|
||||
files[] = views/handlers/tmgmt_node_handler_filter_node_translatable_types.inc
|
||||
files[] = views/handlers/tmgmt_node_handler_filter_missing_translation.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Source plugin for the Translation Management system that handles nodes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_plugin_info().
|
||||
*/
|
||||
function tmgmt_node_tmgmt_source_plugin_info() {
|
||||
$info['node'] = array(
|
||||
'label' => t('Node'),
|
||||
'description' => t('Source handler for nodes.'),
|
||||
'plugin controller class' => 'TMGMTNodeSourcePluginController',
|
||||
'ui controller class' => 'TMGMTNodeSourceUIController',
|
||||
'views controller class' => 'TMGMTNodeSourceViewsController',
|
||||
'item types' => array(),
|
||||
);
|
||||
foreach (node_type_get_names() as $type => $name) {
|
||||
if (translation_supported_type($type)) {
|
||||
$info['node']['item types'][$type] = $name;
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validator for the missing target language views field handler.
|
||||
*/
|
||||
function tmgmt_node_views_exposed_target_language_validate($form, &$form_state) {
|
||||
if (!empty($form_state['values']['tmgmt_node_missing_translation']) && $form_state['values']['language_1'] == $form_state['values']['tmgmt_node_missing_translation']) {
|
||||
form_set_error('tmgmt_node_missing_translation', t('The source and target languages must not be the same.'));
|
||||
}
|
||||
}
|
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the node source plugin controller.
|
||||
*/
|
||||
|
||||
class TMGMTNodeSourcePluginController extends TMGMTDefaultSourcePluginController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Returns the data from the fields as a structure that can be processed by
|
||||
* the Translation Management system.
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
$node = node_load($job_item->item_id);
|
||||
$source_language = $job_item->getJob()->source_language;
|
||||
$languages = language_list();
|
||||
|
||||
// If the node language is not the same as the job source language try to
|
||||
// load its translation for the job source language.
|
||||
if ($node->language != $source_language) {
|
||||
$translation_loaded = FALSE;
|
||||
foreach (translation_node_get_translations($node->nid) as $language => $translation) {
|
||||
if ($language == $source_language) {
|
||||
$node = node_load($translation->nid);
|
||||
$translation_loaded = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$translation_loaded) {
|
||||
throw new TMGMTException(t('Unable to load %language translation for the node %title',
|
||||
array('%language' => $languages[$source_language]->name, '%title' => $node->title)));
|
||||
}
|
||||
}
|
||||
|
||||
$type = node_type_get_type($node);
|
||||
// Get all the fields that can be translated and arrange their values into
|
||||
// a specific structure.
|
||||
$structure = tmgmt_field_get_source_data('node', $node, $job_item->getJob()->source_language);
|
||||
$structure['node_title']['#label'] = $type->title_label;
|
||||
$structure['node_title']['#text'] = $node->title;
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
$job = $job_item->getJob();
|
||||
if (empty($node->tnid)) {
|
||||
// We have no translation source nid, this is a new set, so create it.
|
||||
$node->tnid = $node->nid;
|
||||
node_save($node);
|
||||
}
|
||||
$translations = translation_node_get_translations($node->tnid);
|
||||
if (isset($translations[$job->target_language])) {
|
||||
// We have already a translation for the source node for the target
|
||||
// language, so load it.
|
||||
$tnode = node_load($translations[$job->target_language]->nid);
|
||||
}
|
||||
else {
|
||||
// We don't have a translation for the source node yet, so create one.
|
||||
$tnode = clone $node;
|
||||
unset($tnode->nid, $tnode->vid, $tnode->uuid, $tnode->vuuid);
|
||||
$tnode->language = $job->target_language;
|
||||
$tnode->translation_source = $node;
|
||||
}
|
||||
|
||||
// Allow modules and translator plugins to alter, for example in the
|
||||
// case of creating revisions for translated nodes, or altering
|
||||
// properties of the tnode before saving.
|
||||
drupal_alter('tmgmt_before_update_node_translation', $tnode, $node, $job_item);
|
||||
|
||||
// Time to put the translated data into the node.
|
||||
$data = $job_item->getData();
|
||||
// Special case for the node title.
|
||||
if (isset($data['node_title']['#translation']['#text'])) {
|
||||
$tnode->title = $data['node_title']['#translation']['#text'];
|
||||
unset($data['node_title']);
|
||||
}
|
||||
tmgmt_field_populate_entity('node', $tnode, $job->target_language, $data, FALSE);
|
||||
// Reset translation field, which determines outdated status.
|
||||
$tnode->translation['status'] = 0;
|
||||
node_save($tnode);
|
||||
|
||||
// We just saved the translation, set the sate of the job item to
|
||||
// 'finished'.
|
||||
$job_item->accepted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
return entity_label('node', $node);
|
||||
}
|
||||
return parent::getLabel($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUri(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
return entity_uri('node', $node);
|
||||
}
|
||||
return parent::getUri($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
return node_type_get_name($node);
|
||||
}
|
||||
return parent::getType($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
return entity_language('node', $node);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
$existing_lang_codes = array();
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
$existing_lang_codes = array(entity_language('node', $node));
|
||||
}
|
||||
if ($translations = translation_node_get_translations($job_item->item_id)) {
|
||||
$existing_lang_codes = array_unique(array_merge($existing_lang_codes, array_keys($translations)));
|
||||
}
|
||||
|
||||
return $existing_lang_codes;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* Basic Node Source tests.
|
||||
*/
|
||||
class TMGMTNodeSourceTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node Source tests',
|
||||
'description' => 'Exporting source data from nodes and saving translations back to nodes',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_node', 'translation'));
|
||||
$this->loginAsAdmin();
|
||||
$this->setEnvironment('de');
|
||||
$this->createNodeType('page', 'Basic page', TRANSLATION_ENABLED, FALSE);
|
||||
$this->attachFields('node', 'page', array(TRUE, TRUE, FALSE, FALSE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests nodes field translation.
|
||||
*/
|
||||
function testNodeSource() {
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$node = $this->createNode('page');
|
||||
// Create a job item for this node and add it to the job.
|
||||
$item = $job->addItem('node', 'node', $node->nid);
|
||||
$this->assertEqual('Basic page', $item->getSourceType());
|
||||
}
|
||||
|
||||
// Translate the job.
|
||||
$job->requestTranslation();
|
||||
|
||||
foreach ($job->getItems() as $item) {
|
||||
// The source is only available in en.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
|
||||
$item->acceptTranslation();
|
||||
$node = node_load($item->item_id);
|
||||
// Check if the tnid attribute is bigger than 0.
|
||||
$this->assertTrue($node->tnid > 0, 'The source node is part of a translation set.');
|
||||
// The translations may be statically cached, so make make sure
|
||||
// to reset the cache before loading the node translations.
|
||||
$cached_translations = & drupal_static('translation_node_get_translations', array());
|
||||
unset($cached_translations[$node->tnid]);
|
||||
// Load the translation set of the source node.
|
||||
$translations = translation_node_get_translations($node->tnid);
|
||||
$this->assertNotNull($translations['de'], 'Translation found for the source node.');
|
||||
if (isset($translations['de'])) {
|
||||
$tnode = node_load($translations['de']->nid, NULL, TRUE);
|
||||
$this->checkTranslatedData($tnode, $item->getData(), 'de');
|
||||
}
|
||||
|
||||
// The source should be now available for en and de.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the source is able to pull content in requested language.
|
||||
*/
|
||||
function testRequestDataForSpecificLanguage() {
|
||||
$this->setEnvironment('sk');
|
||||
$this->setEnvironment('es');
|
||||
$content_type = $this->drupalCreateContentType();
|
||||
|
||||
$node = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(),
|
||||
'language' => 'sk',
|
||||
'body' => array('sk' => array(array())),
|
||||
'type' => $content_type->type,
|
||||
));
|
||||
|
||||
$this->drupalCreateNode(array(
|
||||
'title' => 'en translation',
|
||||
'language' => 'en',
|
||||
'tnid' => $node->nid,
|
||||
'body' => array('en' => array(array())),
|
||||
'type' => $content_type->type,
|
||||
));
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob('en', 'de');
|
||||
$job->save();
|
||||
$job->addItem('node', 'node', $node->nid);
|
||||
|
||||
$data = $job->getData();
|
||||
$this->assertEqual($data[1]['node_title']['#text'], 'en translation');
|
||||
|
||||
// Create new job item with a source language for which the translation
|
||||
// does not exit.
|
||||
$job = $this->createJob('es', 'cs');
|
||||
$job->save();
|
||||
try {
|
||||
$job->addItem('node', 'node', $node->nid);
|
||||
$this->fail('The job item should not be added as there is no translation for language "es"');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$languages = language_list();
|
||||
$this->assertEqual(t('Unable to load %language translation for the node %title',
|
||||
array('%language' => $languages['es']->name, '%title' => $node->title)), $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the data from an entity with the translated data.
|
||||
*
|
||||
* @param $node
|
||||
* The translated node object.
|
||||
* @param $data
|
||||
* An array with the translated data.
|
||||
* @param $langcode
|
||||
* The code of the target language.
|
||||
*/
|
||||
function checkTranslatedData($node, $data, $langcode) {
|
||||
foreach (element_children($data) as $field_name) {
|
||||
if ($field_name == 'node_title') {
|
||||
$this->assertEqual($node->title, $data['node_title']['#translation']['#text'], 'The title of the translated node matches the translated data.');
|
||||
continue;
|
||||
}
|
||||
foreach (element_children($data[$field_name]) as $delta) {
|
||||
$field_langcode = field_is_translatable('node', field_info_field($field_name)) ? $langcode : LANGUAGE_NONE;
|
||||
foreach (element_children($data[$field_name][$delta]) as $column) {
|
||||
$column_value = $data[$field_name][$delta][$column];
|
||||
if (!isset($column_value['#translate']) || $column_value['#translate']) {
|
||||
$this->assertEqual($node->{$field_name}[$field_langcode][$delta][$column], $column_value['#translation']['#text'], format_string('The translatable field %field:%delta has been populated with the proper translated data.', array(
|
||||
'%field' => $field_name,
|
||||
'delta' => $delta
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the node source ui controller.
|
||||
*/
|
||||
|
||||
class TMGMTNodeSourceUIController extends TMGMTDefaultSourceUIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_menu() {
|
||||
// The node source overview is a View using Views Bulk Operations. Therefore
|
||||
// we don't need to provide any menu items.
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_forms() {
|
||||
// The node source overview is a View using Views Bulk Operations. Therefore
|
||||
// we don't need to provide any forms.
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_views_default_views() {
|
||||
return _tmgmt_load_exports('tmgmt_node', 'views', 'view.inc', 'view');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
name = Content Source User Interface
|
||||
description = User Interface for the content translation source plugin.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = tmgmt_node
|
||||
dependencies[] = tmgmt_ui
|
||||
dependencies[] = views_bulk_operations
|
||||
|
||||
files[] = tmgmt_node_ui.test
|
||||
files[] = tmgmt_node_ui.overview.test
|
||||
|
||||
; Views handlers
|
||||
files[] = views/tmgmt_node_ui_handler_filter_node_translatable_types.inc
|
||||
files[] = views/tmgmt_node_ui_handler_field_jobs.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main module file for the translation management node source plugin user
|
||||
* interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_page_alter().
|
||||
*/
|
||||
function tmgmt_node_ui_page_alter(&$page) {
|
||||
// Translation tabs for nodes.
|
||||
if (($node = menu_get_object()) && entity_access('create', 'tmgmt_job')) {
|
||||
if (isset($page['content']['system_main']['translation_node_overview'])) {
|
||||
module_load_include('inc', 'tmgmt_node_ui', 'tmgmt_node_ui.pages');
|
||||
$page['content']['system_main']['translation_node_overview'] = drupal_get_form('tmgmt_node_ui_node_form', $node, $page['content']['system_main']['translation_node_overview']);
|
||||
}
|
||||
// Support the context module: when context is used for placing the
|
||||
// system_main block, then block contents appear nested one level deeper
|
||||
// under another 'content' key.
|
||||
elseif (isset($page['content']['system_main']['content']['translation_node_overview'])) {
|
||||
module_load_include('inc', 'tmgmt_node_ui', 'tmgmt_node_ui.pages');
|
||||
$page['content']['system_main']['content']['translation_node_overview'] = drupal_get_form('tmgmt_node_ui_node_form', $node, $page['content']['system_main']['content']['translation_node_overview']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*/
|
||||
function tmgmt_node_ui_action_info() {
|
||||
return array(
|
||||
'tmgmt_node_ui_checkout_multiple_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Request translations'),
|
||||
'configurable' => false,
|
||||
'aggregate' => true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to do multistep checkout for translations.
|
||||
*
|
||||
* @param array $nodes
|
||||
* Array of Drupal nodes.
|
||||
* @param $info
|
||||
* Action info - not used.
|
||||
*
|
||||
*/
|
||||
function tmgmt_node_ui_checkout_multiple_action($nodes, $info) {
|
||||
$jobs = array();
|
||||
$source_lang_registry = array();
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($nodes as $node) {
|
||||
|
||||
try {
|
||||
|
||||
// For given source lang no job exists yet.
|
||||
if (!isset($source_lang_registry[$node->language])) {
|
||||
// Create new job.
|
||||
$job = tmgmt_job_create($node->language, NULL, $GLOBALS['user']->uid);
|
||||
// Add initial job item.
|
||||
$job->addItem('node', 'node', $node->nid);
|
||||
// Add job identifier into registry
|
||||
$source_lang_registry[$node->language] = $job->tjid;
|
||||
// Add newly created job into jobs queue.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
// We have a job for given source lang, so just add new job item for the
|
||||
// existing job.
|
||||
else {
|
||||
$jobs[$source_lang_registry[$node->language]]->addItem('node', 'node', $node->nid);
|
||||
}
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
watchdog_exception('tmgmt', $e);
|
||||
drupal_set_message(t('Unable to add job item for node %name. Make sure the source content is not empty.', array('%name' => $node->title)), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// If necessary, do a redirect.
|
||||
$redirects = tmgmt_ui_job_checkout_multiple($jobs);
|
||||
if ($redirects) {
|
||||
tmgmt_ui_redirect_queue_set($redirects, current_path());
|
||||
drupal_set_message(format_plural(count($redirects), t('One job needs to be checked out.'), t('@count jobs need to be checked out.')));
|
||||
drupal_goto(tmgmt_ui_redirect_queue_dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Content Overview Tests
|
||||
*/
|
||||
class TMGMTNodeSourceUIOverviewTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node Source UI Overview tests',
|
||||
'description' => 'Tests the user interface for node overviews.',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('rules'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_node_ui'));
|
||||
|
||||
$this->loginAsAdmin();
|
||||
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('fr');
|
||||
$this->setEnvironment('es');
|
||||
$this->setEnvironment('el');
|
||||
|
||||
$this->createNodeType('page', 'Page', TRANSLATION_ENABLED, FALSE);
|
||||
// 1 means that the node type can have a language but is not translatable.
|
||||
$this->createNodeType('untranslated', 'Untranslated', 1, FALSE);
|
||||
|
||||
$this->checkPermissions(array(), TRUE);
|
||||
|
||||
// Allow auto-accept.
|
||||
$default_translator = tmgmt_translator_load('test_translator');
|
||||
$default_translator->settings = array(
|
||||
'auto_accept' => TRUE,
|
||||
);
|
||||
$default_translator->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translating through the content source overview.
|
||||
*/
|
||||
function testNodeSourceOverview() {
|
||||
|
||||
// Login as translator to translate nodes.
|
||||
$this->loginAsTranslator(array(
|
||||
'translate content',
|
||||
'edit any page content',
|
||||
'create page content',
|
||||
));
|
||||
|
||||
// Create a bunch of english nodes.
|
||||
$node1 = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
$node2 = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
$node3 = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
$node4 = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Create a node with an undefined language.
|
||||
$node5 = $this->drupalCreateNode(array('type' => 'page'));
|
||||
|
||||
// Create a node of an untranslatable content type.
|
||||
$node6 = $this->drupalCreateNode(array('type' => 'untranslated', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the overview page and make sure the nodes are there.
|
||||
$this->drupalGet('admin/tmgmt/sources/node');
|
||||
|
||||
// Make sure that valid nodes are shown.
|
||||
$this->assertText($node1->title);
|
||||
$this->assertText($node2->title);
|
||||
$this->assertText($node3->title);
|
||||
$this->assertText($node4->title);
|
||||
// Nodes without a language must not be shown.
|
||||
$this->assertNoText($node5->title);
|
||||
// Node with a type that is not enabled for translation must not be shown.
|
||||
$this->assertNoText($node6->title);
|
||||
|
||||
// Now translate them.
|
||||
$edit = array(
|
||||
'views_bulk_operations[0]' => TRUE,
|
||||
'views_bulk_operations[1]' => TRUE,
|
||||
'views_bulk_operations[2]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translations'));
|
||||
|
||||
// Some assertions on the submit form.
|
||||
$this->assertText(t('@title and 2 more (English to ?, Unprocessed)', array('@title' => $node1->title)));
|
||||
$this->assertText($node1->title);
|
||||
$this->assertText($node2->title);
|
||||
$this->assertText($node3->title);
|
||||
$this->assertNoText($node4->title);
|
||||
|
||||
// Translate
|
||||
$edit = array(
|
||||
'target_language' => 'de',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Submit to translator'));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node1->title, '@language' => t('German'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node1->title)));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node2->title, '@language' => t('German'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node1->title)));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node3->title, '@language' => t('German'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node1->title)));
|
||||
|
||||
// Check the translated node.
|
||||
$this->clickLink($node1->title);
|
||||
$this->clickLink(t('Translate'));
|
||||
$this->assertText('de_' . $node1->title);
|
||||
|
||||
// Test for the source list limit set in the views export.
|
||||
$view = views_get_view('tmgmt_node_source_overview');
|
||||
$view->execute_display('default');
|
||||
$this->assertEqual($view->get_items_per_page(), variable_get('tmgmt_source_list_limit', 20));
|
||||
|
||||
// Test the missing translation filter.
|
||||
|
||||
// Create nodes needed to test the missing translation filter here so that
|
||||
// VBO order is not affected.
|
||||
$node_not_translated = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
$node_de = $this->drupalCreateNode(array('type' => 'page', 'language' => 'de', 'body' => array('de' => array(array()))));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/node');
|
||||
$this->assertText($node1->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
$this->assertText($node_de->title);
|
||||
|
||||
// Submitting the search form will not work. After the form submission the
|
||||
// page does gets redirected to url without query parameters. So we simply
|
||||
// access the page with desired query.
|
||||
$this->drupalGet('admin/tmgmt/sources/node', array('query' => array(
|
||||
'tmgmt_node_missing_translation' => 'de',
|
||||
'target_status' => 'untranslated',
|
||||
)));
|
||||
$this->assertNoText($node1->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
$this->assertNoText($node_de->title);
|
||||
|
||||
// Update the the translate flag of the translated node and test if it is
|
||||
// listed among sources with missing translation.
|
||||
db_update('node')->fields(array('translate' => 1))
|
||||
->condition('nid', $node1->nid)->execute();
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/node', array('query' => array(
|
||||
'tmgmt_node_missing_translation' => 'de',
|
||||
'target_status' => 'outdated',
|
||||
)));
|
||||
$this->assertText($node1->title);
|
||||
$this->assertNoText($node_not_translated->title);
|
||||
$this->assertNoText($node_de->title);
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/node', array('query' => array(
|
||||
'tmgmt_node_missing_translation' => 'de',
|
||||
'target_status' => 'untranslated_or_outdated',
|
||||
)));
|
||||
$this->assertText($node1->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
$this->assertNoText($node_de->title);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides page and form callbacks for the Translation Management Tool Node
|
||||
* Source User Interface module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Node translation overview form. This form overrides the Drupal core or i18n
|
||||
* Content Translation page with a tableselect form.
|
||||
*/
|
||||
function tmgmt_node_ui_node_form($form, &$form_state, $node, $original) {
|
||||
// Store the node in the form state so we can easily create the job in the
|
||||
// submit handler.
|
||||
$form_state['node'] = $node;
|
||||
|
||||
$form['top_actions']['#type'] = 'actions';
|
||||
$form['top_actions']['#weight'] = -10;
|
||||
tmgmt_ui_add_cart_form($form['top_actions'], $form_state, 'node', 'node', $node->nid);
|
||||
|
||||
// Inject our additional column into the header.
|
||||
array_splice($original['#header'], -1, 0, array(t('Pending Translations')));
|
||||
// Make this a tableselect form.
|
||||
$form['languages'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $original['#header'],
|
||||
'#options' => array(),
|
||||
);
|
||||
$languages = module_exists('i18n_node') ? i18n_node_language_list($node) : language_list();
|
||||
// Check if there is a job / job item that references this translation.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, $node->language);
|
||||
foreach ($languages as $langcode => $language) {
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
// Never show language neutral on the overview.
|
||||
continue;
|
||||
}
|
||||
// Since the keys are numeric and in the same order we can shift one element
|
||||
// after the other from the original non-form rows.
|
||||
$option = array_shift($original['#rows']);
|
||||
if ($langcode == $node->language) {
|
||||
$additional = '<strong>' . t('Source') . '</strong>';
|
||||
// This is the source object so we disable the checkbox for this row.
|
||||
$form['languages'][$langcode] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
elseif (isset($items[$langcode])) {
|
||||
/** @var TMGMTJobItem $item */
|
||||
$item = $items[$langcode];
|
||||
if ($item->getJob()->isUnprocessed()) {
|
||||
$uri = $item->getJob()->uri();
|
||||
$additional = l(t('Unprocessed'), $uri['path']);
|
||||
}
|
||||
else {
|
||||
$wrapper = entity_metadata_wrapper('tmgmt_job_item', $item);
|
||||
$uri = $item->uri();
|
||||
$additional = l($wrapper->state->label(), $uri['path']);
|
||||
}
|
||||
// Disable the checkbox for this row since there is already a translation
|
||||
// in progress that has not yet been finished. This way we make sure that
|
||||
// we don't stack multiple active translations for the same item on top
|
||||
// of each other.
|
||||
$form['languages'][$langcode] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
else {
|
||||
// There is no translation job / job item for this target language.
|
||||
$additional = t('None');
|
||||
}
|
||||
// Inject the additional column into the array.
|
||||
array_splice($option, -1, 0, array($additional));
|
||||
// Append the current option array to the form.
|
||||
$form['languages']['#options'][$langcode] = $option;
|
||||
}
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['request'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Request translation'),
|
||||
'#submit' => array('tmgmt_node_ui_translate_form_submit'),
|
||||
'#validate' => array('tmgmt_node_ui_translate_form_validate'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback for the node translation overview form.
|
||||
*/
|
||||
function tmgmt_node_ui_translate_form_validate($form, &$form_state) {
|
||||
$selected = array_filter($form_state['values']['languages']);
|
||||
if (empty($selected)) {
|
||||
form_set_error('languages', t('You have to select at least one language for requesting a translation.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit callback for the node translation overview form.
|
||||
*/
|
||||
function tmgmt_node_ui_translate_form_submit($form, &$form_state) {
|
||||
$node = $form_state['node'];
|
||||
$values = $form_state['values'];
|
||||
$jobs = array();
|
||||
foreach (array_keys(array_filter($values['languages'])) as $langcode) {
|
||||
// Create the job object.
|
||||
$job = tmgmt_job_create($node->language, $langcode, $GLOBALS['user']->uid);
|
||||
// Add the job item.
|
||||
$job->addItem('node', 'node', $node->nid);
|
||||
// Append this job to the array of created jobs so we can redirect the user
|
||||
// to a multistep checkout form if necessary.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
tmgmt_ui_job_checkout_and_redirect($form_state, $jobs);
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains default rules.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_default_rules_configuration().
|
||||
*/
|
||||
function tmgmt_node_ui_default_rules_configuration() {
|
||||
$data = '{ "tmgmt_node_ui_request_translation" : {
|
||||
"LABEL" : "Request translation",
|
||||
"PLUGIN" : "rule",
|
||||
"REQUIRES" : [ "tmgmt" ],
|
||||
"USES VARIABLES" : { "nodes" : { "label" : "Nodes", "type" : "list\u003Cnode\u003E" } },
|
||||
"DO" : [
|
||||
{ "tmgmt_get_first_from_node_list" : {
|
||||
"USING" : { "list" : [ "nodes" ] },
|
||||
"PROVIDE" : { "first_node" : { "first_node" : "Node" } }
|
||||
}
|
||||
},
|
||||
{ "tmgmt_rules_create_job" : {
|
||||
"USING" : { "source_language" : [ "first-node:language" ] },
|
||||
"PROVIDE" : { "job" : { "job" : "Job" } }
|
||||
}
|
||||
},
|
||||
{ "LOOP" : {
|
||||
"USING" : { "list" : [ "nodes" ] },
|
||||
"ITEM" : { "node" : "Node" },
|
||||
"DO" : [
|
||||
{ "tmgmt_rules_job_add_item" : {
|
||||
"job" : [ "job" ],
|
||||
"plugin" : "node",
|
||||
"item_type" : "node",
|
||||
"item_id" : [ "node:nid" ]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{ "tmgmt_rules_job_checkout" : { "job" : [ "job" ] } }
|
||||
]
|
||||
}
|
||||
}';
|
||||
$rule = rules_import($data);
|
||||
$configs[$rule->name] = $rule;
|
||||
return $configs;
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
#edit-tmgmt-node-missing-translation-wrapper .form-item-target-status,
|
||||
#edit-tmgmt-node-missing-translation-wrapper .form-item-tmgmt-node-missing-translation {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#edit-tmgmt-node-missing-translation-wrapper .form-item-target-status {
|
||||
margin: 0 0 0 55px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
top: -19px;
|
||||
}
|
||||
|
||||
#edit-tmgmt-node-missing-translation-wrapper .form-item-target-status select {
|
||||
margin-top: 9px;
|
||||
}
|
@@ -0,0 +1,450 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Node Source UI tests.
|
||||
*/
|
||||
class TMGMTNodeSourceUITestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node Source UI tests',
|
||||
'description' => 'Tests the user interface for node translation sources.',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_node_ui', 'block'));
|
||||
|
||||
// We need the administer blocks permission.
|
||||
$this->loginAsAdmin(array('administer blocks'));
|
||||
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('fr');
|
||||
$this->setEnvironment('es');
|
||||
$this->setEnvironment('el');
|
||||
|
||||
// @todo Re-enable this when switching to testing profile.
|
||||
// Enable the main page content block for hook_page_alter() to work.
|
||||
$edit = array(
|
||||
'blocks[system_main][region]' => 'content',
|
||||
);
|
||||
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
|
||||
|
||||
$this->createNodeType('page', 'Page', TRANSLATION_ENABLED, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the create, submit and accept permissions.
|
||||
*/
|
||||
function testPermissions() {
|
||||
|
||||
$no_permissions = $this->drupalCreateUser();
|
||||
$this->drupalLogin($no_permissions);
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Test with a user that is only allowed to create jobs.
|
||||
$create_user = $this->drupalCreateUser(array('access administration pages', 'translate content', 'create translation jobs'));
|
||||
$this->drupalLogin($create_user);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
$this->assertText(t('One job has been created.'));
|
||||
// Verify that we are still on the translate tab.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
|
||||
// The job is unprocessed, check the status flag in the source list.
|
||||
$this->drupalGet('admin/tmgmt/sources');
|
||||
$links = $this->xpath('//a[contains(@title, :title)]', array(':title' => t('Active job item: @state', array('@state' => t('Unprocessed')))));
|
||||
$attributes = $links[0]->attributes();
|
||||
// Check if the found link points to the job checkout page instead of the
|
||||
// job item review form.
|
||||
$this->assertEqual($attributes['href'], url('admin/tmgmt/jobs/1', array('query' => array('destination' => 'admin/tmgmt/sources'))));
|
||||
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLink(t('manage'));
|
||||
$this->assertNoLink(t('submit'));
|
||||
$this->assertNoLink(t('delete'));
|
||||
$this->assertText(t('@title', array('@title' => $node->title)));
|
||||
$this->clickLink(t('manage'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoRaw(t('Submit to translator'));
|
||||
|
||||
// Try to access the delete page directly.
|
||||
$this->drupalGet($this->getUrl() . '/delete');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Log in as user with only submit permission.
|
||||
$submit_user = $this->drupalCreateUser(array('access administration pages', 'translate content', 'submit translation jobs'));
|
||||
$this->drupalLogin($submit_user);
|
||||
|
||||
// Go to the translate tab, verify that there is no request translation
|
||||
// button.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
$this->assertNoRaw(t('Request translation'));
|
||||
|
||||
// Go to the overview and submit the job.
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLink(t('submit'));
|
||||
$this->assertNoLink(t('manage'));
|
||||
$this->assertNoLink(t('delete'));
|
||||
$this->assertText(t('@title', array('@title' => $node->title)));
|
||||
|
||||
// Check VBO actions - "submit translation job" has the right to cancel
|
||||
// translation only.
|
||||
$element = $this->xpath('//select[@id=:id]/option/@value', array(':id' => 'edit-operation'));
|
||||
$options = array();
|
||||
foreach ($element as $option) {
|
||||
$options[] = (string) $option;
|
||||
}
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_abort_translation', $options));
|
||||
|
||||
// Go to the job checkout page and submit it.
|
||||
$this->clickLink('submit');
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
// After submit the redirect goes back to the job overview.
|
||||
$this->assertUrl('admin/tmgmt');
|
||||
|
||||
// Make sure that the job is active now.
|
||||
$this->assertText(t('Active'));
|
||||
// Click abort link and check if we are at the job abort confirm page.
|
||||
$this->clickLink(t('abort'));
|
||||
$this->assertText(t('This will send a request to the translator to abort the job. After the action the job translation process will be aborted and only remaining action will be resubmitting it.'));
|
||||
// Return back to job overview and test the manage link.
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
$this->clickLink(t('manage'));
|
||||
$this->assertText(t('Needs review'));
|
||||
$this->assertNoLink(t('review'));
|
||||
|
||||
// Now log in as user with only accept permission and review the job.
|
||||
$accept_user = $this->drupalCreateUser(array('access administration pages', 'accept translation jobs'));
|
||||
$this->drupalLogin($accept_user);
|
||||
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
|
||||
// Check VBO actions - "accept translation jobs" has the right to accept
|
||||
// translation only.
|
||||
$element = $this->xpath('//select[@id=:id]/option/@value', array(':id' => 'edit-operation'));
|
||||
$options = array();
|
||||
foreach ($element as $option) {
|
||||
$options[] = (string) $option;
|
||||
}
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_accept_translation', $options));
|
||||
|
||||
$this->clickLink('manage');
|
||||
$this->clickLink('review');
|
||||
|
||||
$this->drupalPost(NULL, array(), '✓');
|
||||
// Verify that the accepted character is shown.
|
||||
$this->assertText('☑');
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
$this->assertText(t('Accepted'));
|
||||
$this->assertText('1/0/0');
|
||||
|
||||
$create_user = $this->loginAsAdmin();
|
||||
$this->drupalLogin($create_user);
|
||||
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
// Check VBO actions - "administer tmgmt" has rights for all actions.
|
||||
$element = $this->xpath('//select[@id=:id]/option/@value', array(':id' => 'edit-operation'));
|
||||
$options = array();
|
||||
foreach ($element as $option) {
|
||||
$options[] = (string) $option;
|
||||
}
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_accept_translation', $options));
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_abort_translation', $options));
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_delete', $options));
|
||||
|
||||
// Go to the translate tab, verify that there is no request translation
|
||||
// button.
|
||||
//$this->drupalGet('node/' . $node->nid);
|
||||
//$this->clickLink('Translate');
|
||||
//$this->assertNoRaw(t('Request translation'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testTranslateTabSingleCheckout() {
|
||||
|
||||
// Create a user that is allowed to translate nodes.
|
||||
$translater = $this->drupalCreateUser(array('translate content', 'create translation jobs', 'submit translation jobs', 'accept translation jobs'));
|
||||
$this->drupalLogin($translater);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
|
||||
// Go to the translate tab and check if the pending translation label is
|
||||
// "Unprocessed" and links to the job checkout page.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->assertLink(t('Unprocessed'));
|
||||
$this->clickLink(t('Unprocessed'));
|
||||
|
||||
// Submit.
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('German'))));
|
||||
|
||||
// Review.
|
||||
$this->clickLink(t('Needs review'));
|
||||
|
||||
// @todo Review job throuh the UI.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, 'en');
|
||||
$items['de']->acceptTranslation();
|
||||
|
||||
// German node should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('de_' . $node->title);
|
||||
|
||||
// Test that the destination query argument does not break the redirect
|
||||
// and we are redirected back to the correct page.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate', array('query' => array('destination' => 'node')));
|
||||
|
||||
// Request a spanish translation.
|
||||
$edit = array(
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the checkout page.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the originally defined destination URL.
|
||||
$this->assertEqual(url('node', array('absolute' => TRUE)), $this->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testTranslateTabMultipeCheckout() {
|
||||
// Create a user that is allowed to translate nodes.
|
||||
$translater = $this->drupalCreateUser(array('translate content', 'create translation jobs', 'submit translation jobs', 'accept translation jobs'));
|
||||
$this->drupalLogin($translater);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('2 jobs need to be checked out.'));
|
||||
|
||||
// Submit all jobs.
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator and continue'));
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('Spanish'))));
|
||||
|
||||
// Review.
|
||||
$this->clickLink(t('Needs review'));
|
||||
|
||||
// @todo Review job throuh the UI.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, 'en');
|
||||
$items['de']->acceptTranslation();
|
||||
$items['es']->acceptTranslation();
|
||||
|
||||
// Translated nodes should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('de_' . $node->title);
|
||||
|
||||
// Translated nodes should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('es_' . $node->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testTranslateTabAutomatedCheckout() {
|
||||
// Hide settings on the test translator.
|
||||
$default_translator = tmgmt_translator_load('test_translator');
|
||||
$default_translator->settings = array(
|
||||
'expose_settings' => FALSE,
|
||||
);
|
||||
$default_translator->save();
|
||||
|
||||
// Create a user that is allowed to translate nodes.
|
||||
$translater = $this->drupalCreateUser(array('translate content', 'create translation jobs', 'submit translation jobs', 'accept translation jobs'));
|
||||
$this->drupalLogin($translater);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertNoText(t('One job needs to be checked out.'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('German'))));
|
||||
|
||||
// Review.
|
||||
$this->clickLink(t('Needs review'));
|
||||
|
||||
// @todo Review job throuh the UI.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, 'en');
|
||||
$items['de']->acceptTranslation();
|
||||
|
||||
// German node should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('de_' . $node->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testTranslateTabDisabledQuickCheckout() {
|
||||
variable_set('tmgmt_quick_checkout', FALSE);
|
||||
|
||||
// Hide settings on the test translator.
|
||||
$default_translator = tmgmt_translator_load('test_translator');
|
||||
$default_translator->settings = array(
|
||||
'expose_settings' => FALSE,
|
||||
);
|
||||
$default_translator->save();
|
||||
|
||||
// Create a user that is allowed to translate nodes.
|
||||
$translater = $this->drupalCreateUser(array('translate content', 'create translation jobs', 'submit translation jobs', 'accept translation jobs'));
|
||||
$this->drupalLogin($translater);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
|
||||
// Submit.
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('German'))));
|
||||
|
||||
// Review.
|
||||
$this->clickLink(t('Needs review'));
|
||||
|
||||
// @todo Review job throuh the UI.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, 'en');
|
||||
$items['de']->acceptTranslation();
|
||||
|
||||
// German node should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('de_' . $node->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the node source specific cart functionality.
|
||||
*/
|
||||
function testCart() {
|
||||
$nodes = array();
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$nodes[] = $this->createNode('page');
|
||||
}
|
||||
|
||||
$this->loginAsAdmin(array_merge($this->translator_permissions, array('translate content')));
|
||||
|
||||
// Test the source overview.
|
||||
$this->drupalPost('admin/tmgmt/sources/node', array(
|
||||
'views_bulk_operations[0]' => TRUE,
|
||||
'views_bulk_operations[1]' => TRUE,
|
||||
), t('Add to cart'));
|
||||
$this->drupalGet('admin/tmgmt/cart');
|
||||
$this->assertText($nodes[0]->title);
|
||||
$this->assertText($nodes[1]->title);
|
||||
|
||||
// Test the translate tab.
|
||||
$this->drupalGet('node/' . $nodes[3]->nid . '/translate');
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a>.',
|
||||
array('@count' => 2, '@url' => url('admin/tmgmt/cart'))));
|
||||
|
||||
$this->drupalPost(NULL, array(), t('Add to cart'));
|
||||
$this->assertRaw(t('@count content source was added into the <a href="@url">cart</a>.', array('@count' => 1, '@url' => url('admin/tmgmt/cart'))));
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a> including the current item.',
|
||||
array('@count' => 3, '@url' => url('admin/tmgmt/cart'))));
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler which shows all jobs which contains a node.
|
||||
*
|
||||
* @TODO: This could probably abstracted into a more generic handler,
|
||||
* or even api function.
|
||||
*/
|
||||
class tmgmt_node_ui_handler_field_jobs extends views_handler_field_prerender_list {
|
||||
function pre_render(&$values) {
|
||||
$nids = array();
|
||||
foreach ($values as $row) {
|
||||
$nid = $this->get_value($row);
|
||||
$nids[] = $nid;
|
||||
}
|
||||
|
||||
$select = db_select('tmgmt_job', 'tj');
|
||||
$select->join('tmgmt_job_item', 'tji', "tj.id = %alias.tjid");
|
||||
$select->join('node', 'n', "tji.item_type = 'node' AND tji.plugin = 'node' AND tji.item_id = node.nid");
|
||||
$select->addField('n', 'nid');
|
||||
$select->addExpression('MAX(tj.id)');
|
||||
|
||||
dpq($select);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler to display the status of all languages.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class tmgmt_node_handler_field_translation_language_status extends views_handler_field {
|
||||
|
||||
/**
|
||||
* @var views_plugin_query_default
|
||||
*/
|
||||
var $query;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $language_items;
|
||||
|
||||
/**
|
||||
* Array of active job items.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $active_job_items = array();
|
||||
|
||||
function init(&$view, &$options) {
|
||||
parent::init($view, $options);
|
||||
$this->view->init_style();
|
||||
$this->additional_fields['nid'] = 'nid';
|
||||
|
||||
/**
|
||||
* Dynamically add new fields so they are used
|
||||
*/
|
||||
$languages = language_list('language');
|
||||
foreach ($languages as $langcode => $lang_info) {
|
||||
$handler = views_get_handler($this->table, $this->field . '_single', 'field');
|
||||
if ($handler) {
|
||||
$id = $options['id'] . '_single_' . $langcode;
|
||||
$this->view->display_handler->handlers['field'][$id] = $handler;
|
||||
$info = array(
|
||||
'id' => $id,
|
||||
'table' => $this->table,
|
||||
'field' => $this->field . '_single',
|
||||
'label' => $lang_info->name,
|
||||
);
|
||||
$handler->langcode = $langcode;
|
||||
$handler->main_field = $options['id'];
|
||||
$handler->init($this->view, $info);
|
||||
$this->language_handlers[$langcode] = $handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pre_render(&$values) {
|
||||
$nids = array();
|
||||
foreach ($values as $value) {
|
||||
$tnid = $this->get_value($value);
|
||||
$tnid = !empty($tnid) ? $tnid : $this->get_value($value, 'nid');
|
||||
$this->active_job_items[$tnid] = tmgmt_job_item_load_latest('node', 'node', $tnid, $value->node_language);
|
||||
$nids[] = $tnid;
|
||||
}
|
||||
if ($nodes = node_load_multiple($nids)) {
|
||||
$result = db_select('node', 'n')
|
||||
->fields('n', array('tnid', 'language', 'translate'))
|
||||
->condition('tnid', $nids)
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
$this->language_items = array();
|
||||
foreach ($result as $tnode) {
|
||||
// The translate flag is set if the translation node is outdated, revert
|
||||
// to have FALSE for outdated translations.
|
||||
$this->language_items[$tnode->tnid][$tnode->language] = !$tnode->translate;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @todo What is this?
|
||||
*/
|
||||
class tmgmt_node_handler_field_translation_language_status_single extends views_handler_field {
|
||||
|
||||
/**
|
||||
* @var tmgmt_node_handler_field_translation_language_status
|
||||
*/
|
||||
var $main_handler;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
var $langcode;
|
||||
|
||||
function init(&$view, &$options) {
|
||||
parent::init($view, $options);
|
||||
$this->additional_fields['nid'] = array(
|
||||
'table' => 'node',
|
||||
'field' => 'nid',
|
||||
);
|
||||
}
|
||||
|
||||
function render($values) {
|
||||
$nid = $values->nid;
|
||||
$langcode = $this->langcode;
|
||||
|
||||
// Check if this is the source language.
|
||||
if ($langcode == $values->node_language) {
|
||||
$translation_status = 'original';
|
||||
}
|
||||
// Check if there is a translation.
|
||||
elseif (!isset($this->view->field[$this->main_field]->language_items[$nid][$langcode])) {
|
||||
$translation_status = 'missing';
|
||||
}
|
||||
// Check if the translation is outdated.
|
||||
elseif (!$this->view->field[$this->main_field]->language_items[$nid][$langcode]) {
|
||||
$translation_status = 'outofdate';
|
||||
}
|
||||
else {
|
||||
$translation_status = 'current';
|
||||
}
|
||||
|
||||
$job_item = NULL;
|
||||
|
||||
if (isset($this->view->field[$this->main_field]->active_job_items[$nid][$langcode])) {
|
||||
$job_item = $this->view->field[$this->main_field]->active_job_items[$nid][$langcode];
|
||||
}
|
||||
|
||||
return theme('tmgmt_ui_translation_language_status_single', array(
|
||||
'translation_status' => $translation_status,
|
||||
'job_item' => $job_item,
|
||||
));
|
||||
}
|
||||
|
||||
function query() {
|
||||
$this->add_additional_fields();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of tmgmt_node_handler_filter_missing_translation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filter by language.
|
||||
*
|
||||
* @ingroup views_filter_handlers
|
||||
*/
|
||||
class tmgmt_node_handler_filter_missing_translation extends views_handler_filter {
|
||||
|
||||
/**
|
||||
* The target status to use for the query.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $target_status = 'untranslated_or_outdated';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function query() {
|
||||
$this->ensure_my_table();
|
||||
|
||||
// Don't do anything if no language was selected.
|
||||
if (!$this->value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$join = new views_join();
|
||||
$join->definition['left_table'] = $this->table_alias;
|
||||
$join->definition['left_field'] = $this->real_field;
|
||||
$join->definition['table'] = 'node';
|
||||
$join->definition['field'] = 'tnid';
|
||||
$join->definition['type'] = 'LEFT';
|
||||
$join->construct();
|
||||
|
||||
$join->extra = array(array(
|
||||
'field' => 'language',
|
||||
'value' => $this->value,
|
||||
));
|
||||
|
||||
$table_alias = $this->query->add_table('node', $this->relationship, $join);
|
||||
|
||||
$this->query->add_where_expression($this->options['group'], "{$this->table_alias}.language != :language", array(':language' => $this->value));
|
||||
|
||||
if ($this->target_status == 'untranslated_or_outdated') {
|
||||
$this->query->add_where_expression($this->options['group'], "($table_alias.nid IS NULL OR {$this->table_alias}.translate = 1)");
|
||||
}
|
||||
elseif ($this->target_status == 'outdated') {
|
||||
$this->query->add_where_expression($this->options['group'], "{$this->table_alias}.translate = 1");
|
||||
}
|
||||
elseif ($this->target_status == 'untranslated') {
|
||||
$this->query->add_where_expression($this->options['group'], "$table_alias.nid IS NULL");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function value_form(&$form, &$form_state) {
|
||||
$options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
|
||||
$form['value'][$identifier] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => $options,
|
||||
'#empty_option' => t('Any'),
|
||||
'#id' => 'tmgmt_node_missing_target_language',
|
||||
'#element_validate' => array('tmgmt_node_views_exposed_target_language_validate'),
|
||||
);
|
||||
// Attach css to style the target_status element inline.
|
||||
$form['#attached']['css'][] = drupal_get_path('module', 'tmgmt_node_ui') . '/tmgmt_node_ui.source_overview.css';
|
||||
$form['value']['target_status'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target status'),
|
||||
'#options' => array(
|
||||
'untranslated_or_outdated' => t('Untranslated or outdated'),
|
||||
'untranslated' => t('Untranslated'),
|
||||
'outdated' => t('Outdated'),
|
||||
),
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
':input[id="tmgmt_node_missing_target_language"]' => array('value' => ''),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function accept_exposed_input($input) {
|
||||
$return = parent::accept_exposed_input($input);
|
||||
if ($return && isset($input['target_status'])) {
|
||||
$this->target_status = $input['target_status'];
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains tmgmt_node_ui_handler_filter_node_translatable_types.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Limits node types to those enabled for content translation.
|
||||
*/
|
||||
class tmgmt_node_ui_handler_filter_node_translatable_types extends views_handler_filter {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function query() {
|
||||
$this->ensure_my_table();
|
||||
$valid_types = array_keys(tmgmt_source_translatable_item_types('node'));
|
||||
if ($valid_types) {
|
||||
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field", array_values($valid_types), 'IN');
|
||||
}
|
||||
else {
|
||||
// There are no valid translatable node types, do not return any results.
|
||||
$this->query->add_where_expression($this->options['group'], '1 = 0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function admin_summary() { }
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Please supply a file description.
|
||||
*/
|
||||
|
||||
class TMGMTNodeSourceViewsController extends TMGMTDefaultSourceViewsController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function views_data() {
|
||||
// Relationships between job items and nodes.
|
||||
$data['tmgmt_job_item']['job_item_to_node'] = array(
|
||||
'title' => t('Content'),
|
||||
'help' => t('Content that is associated with this job item.'),
|
||||
'real field' => 'item_id',
|
||||
'relationship' => array(
|
||||
'label' => t('Content'),
|
||||
'base' => 'node',
|
||||
'base field' => 'vid',
|
||||
'relationship field' => 'item_id',
|
||||
'extra' => array(
|
||||
array(
|
||||
'table' => 'tmgmt_job_item',
|
||||
'field' => 'item_type',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
array(
|
||||
'table' => 'tmgmt_job_item',
|
||||
'field' => 'plugin',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$data['node']['node_to_job_item'] = array(
|
||||
'title' => t('Translation job item'),
|
||||
'help' => t('Job items of this node.'),
|
||||
'relationship' => array(
|
||||
'real field' => 'vid',
|
||||
'label' => t('Translation job item'),
|
||||
'base' => 'tmgmt_job_item',
|
||||
'base field' => 'item_id',
|
||||
'extra' => array(
|
||||
array(
|
||||
'field' => 'item_type',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
array(
|
||||
'field' => 'plugin',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_translatable_types_all'] = array(
|
||||
'group' => t('Content translation'),
|
||||
'title' => t('All translatable types'),
|
||||
'help' => t('Enforces that only nodes from node types which are translatable are '),
|
||||
'filter' => array(
|
||||
'handler' => 'tmgmt_node_ui_handler_filter_node_translatable_types',
|
||||
'real field' => 'type',
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_node_missing_translation'] = array(
|
||||
'group' => t('Content translation'),
|
||||
'title' => t('Missing translation'),
|
||||
'help' => t('Enables the search for nodes with missing translation ofr the specified language'),
|
||||
'filter' => array(
|
||||
'handler' => 'tmgmt_node_handler_filter_missing_translation',
|
||||
'real field' => 'nid',
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_jobs'] = array(
|
||||
'title' => t('Translation jobs'),
|
||||
'help' => t('Shows all translation jobs which contains this node'),
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_node_ui_handler_field_jobs',
|
||||
'real field' => 'nid',
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_job_item'] = array(
|
||||
'title' => t('Job item'),
|
||||
'real field' => 'vid',
|
||||
'relationship' => array(
|
||||
'title' => t('Translation job item'),
|
||||
'label' => t('Translation job item'),
|
||||
'base' => 'tmgmt_job_item',
|
||||
'base field' => 'item_id',
|
||||
'extra' => array(
|
||||
array(
|
||||
'field' => 'item_type',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
array(
|
||||
'field' => 'plugin',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$data['node']['translation_language_status'] = array(
|
||||
'group' => t('Content translation'),
|
||||
'title' => t('All translation languages'),
|
||||
'help' => t('Display all target lanuages.'),
|
||||
'real field' => 'tnid',
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_node_handler_field_translation_language_status',
|
||||
),
|
||||
);
|
||||
$data['node']['translation_language_status_single'] = array(
|
||||
'title' => t('All translation languages (single)'),
|
||||
'help' => t("Don't use this in the user interface."),
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_node_handler_field_translation_language_status_single',
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_translatable_types_select'] = array(
|
||||
'group' => t('Content translation'),
|
||||
'title' => t('Select translatable content types'),
|
||||
'help' => t('Allows to filter on specific translatable types.'),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_in_operator',
|
||||
'real field' => 'type',
|
||||
'options callback' => 'tmgmt_source_translatable_item_types',
|
||||
'options arguments' => array($this->pluginType),
|
||||
),
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
|
||||
$view = new view();
|
||||
$view->name = 'tmgmt_node_source_overview';
|
||||
$view->description = 'Node source overview for bulk operations.';
|
||||
$view->tag = 'Translation Management';
|
||||
$view->base_table = 'node';
|
||||
$view->human_name = 'Node Source Overview';
|
||||
$view->core = 7;
|
||||
$view->api_version = '3.0';
|
||||
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
|
||||
|
||||
/* Display: Master */
|
||||
$handler = $view->new_display('default', 'Master', 'default');
|
||||
$handler->display->display_options['title'] = 'Content overview';
|
||||
$handler->display->display_options['use_more_always'] = FALSE;
|
||||
$handler->display->display_options['group_by'] = TRUE;
|
||||
$handler->display->display_options['access']['type'] = 'perm';
|
||||
$handler->display->display_options['access']['perm'] = 'create translation jobs';
|
||||
$handler->display->display_options['cache']['type'] = 'none';
|
||||
$handler->display->display_options['query']['type'] = 'views_query';
|
||||
$handler->display->display_options['query']['options']['query_comment'] = FALSE;
|
||||
$handler->display->display_options['exposed_form']['type'] = 'basic';
|
||||
$handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
|
||||
$handler->display->display_options['pager']['type'] = 'full';
|
||||
$handler->display->display_options['pager']['options']['items_per_page'] = variable_get('tmgmt_source_list_limit', 20);
|
||||
$handler->display->display_options['pager']['options']['offset'] = '0';
|
||||
$handler->display->display_options['pager']['options']['id'] = '0';
|
||||
$handler->display->display_options['pager']['options']['quantity'] = '9';
|
||||
$handler->display->display_options['style_plugin'] = 'table';
|
||||
$handler->display->display_options['style_options']['columns'] = array(
|
||||
'title' => 'title',
|
||||
);
|
||||
$handler->display->display_options['style_options']['default'] = '-1';
|
||||
$handler->display->display_options['style_options']['info'] = array(
|
||||
'title' => array(
|
||||
'sortable' => 0,
|
||||
'default_sort_order' => 'asc',
|
||||
'align' => '',
|
||||
'separator' => '',
|
||||
'empty_column' => 0,
|
||||
),
|
||||
);
|
||||
/* No results behavior: Global: Text area */
|
||||
$handler->display->display_options['empty']['area']['id'] = 'area';
|
||||
$handler->display->display_options['empty']['area']['table'] = 'views';
|
||||
$handler->display->display_options['empty']['area']['field'] = 'area';
|
||||
$handler->display->display_options['empty']['area']['content'] = 'There are no nodes that match the specified filter criteria.';
|
||||
$handler->display->display_options['empty']['area']['format'] = 'filtered_html';
|
||||
/* Relationship: User */
|
||||
$handler->display->display_options['relationships']['uid']['id'] = 'uid';
|
||||
$handler->display->display_options['relationships']['uid']['table'] = 'node';
|
||||
$handler->display->display_options['relationships']['uid']['field'] = 'uid';
|
||||
$handler->display->display_options['relationships']['uid']['ui_name'] = 'User';
|
||||
$handler->display->display_options['relationships']['uid']['label'] = 'User';
|
||||
/* Field: Bulk operations */
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['id'] = 'views_bulk_operations';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['field'] = 'views_bulk_operations';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['ui_name'] = 'Bulk operations';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['label'] = '<!--views-bulk-operations-select-all-->';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['element_label_colon'] = FALSE;
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['display_type'] = '1';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['enable_select_all_pages'] = 1;
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['force_single'] = 0;
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['entity_load_capacity'] = '10';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_operations'] = array(
|
||||
'rules_component::tmgmt_node_ui_tmgmt_nodes_add_items_to_cart' => array(
|
||||
'selected' => 1,
|
||||
'skip_confirmation' => 1,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_assign_owner_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::views_bulk_operations_delete_item' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::views_bulk_operations_script_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_make_sticky_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_make_unsticky_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::views_bulk_operations_modify_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
'settings' => array(
|
||||
'show_all_tokens' => 1,
|
||||
'display_values' => array(
|
||||
'_all_' => '_all_',
|
||||
),
|
||||
),
|
||||
),
|
||||
'action::views_bulk_operations_argument_selector_action' => array(
|
||||
'selected' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
'settings' => array(
|
||||
'url' => '',
|
||||
),
|
||||
),
|
||||
'action::node_promote_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_publish_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_unpromote_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'rules_component::tmgmt_node_ui_request_translation' => array(
|
||||
'selected' => 0,
|
||||
'skip_confirmation' => 1,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::tmgmt_node_ui_checkout_multiple_action' => array(
|
||||
'selected' => 1,
|
||||
'skip_confirmation' => 1,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_save_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::system_send_email_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_unpublish_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_unpublish_by_keyword_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
);
|
||||
/* Field: Title */
|
||||
$handler->display->display_options['fields']['title']['id'] = 'title';
|
||||
$handler->display->display_options['fields']['title']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['title']['field'] = 'title';
|
||||
$handler->display->display_options['fields']['title']['ui_name'] = 'Title';
|
||||
$handler->display->display_options['fields']['title']['label'] = 'Title (in source language)';
|
||||
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
|
||||
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
|
||||
/* Field: Type */
|
||||
$handler->display->display_options['fields']['type']['id'] = 'type';
|
||||
$handler->display->display_options['fields']['type']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['type']['field'] = 'type';
|
||||
$handler->display->display_options['fields']['type']['ui_name'] = 'Type';
|
||||
/* Field: All translation languages */
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['id'] = 'translation_language_status_1';
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['field'] = 'translation_language_status';
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['ui_name'] = 'All translation languages';
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['exclude'] = TRUE;
|
||||
/* Field: Author */
|
||||
$handler->display->display_options['fields']['name']['id'] = 'name';
|
||||
$handler->display->display_options['fields']['name']['table'] = 'users';
|
||||
$handler->display->display_options['fields']['name']['field'] = 'name';
|
||||
$handler->display->display_options['fields']['name']['relationship'] = 'uid';
|
||||
$handler->display->display_options['fields']['name']['ui_name'] = 'Author';
|
||||
$handler->display->display_options['fields']['name']['label'] = 'Author';
|
||||
/* Field: Updated date */
|
||||
$handler->display->display_options['fields']['changed']['id'] = 'changed';
|
||||
$handler->display->display_options['fields']['changed']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['changed']['field'] = 'changed';
|
||||
$handler->display->display_options['fields']['changed']['ui_name'] = 'Updated date';
|
||||
$handler->display->display_options['fields']['changed']['date_format'] = 'short';
|
||||
/* Sort criterion: Post date */
|
||||
$handler->display->display_options['sorts']['created']['id'] = 'created';
|
||||
$handler->display->display_options['sorts']['created']['table'] = 'node';
|
||||
$handler->display->display_options['sorts']['created']['field'] = 'created';
|
||||
$handler->display->display_options['sorts']['created']['ui_name'] = 'Post date';
|
||||
$handler->display->display_options['sorts']['created']['order'] = 'DESC';
|
||||
/* Filter criterion: Content: Title */
|
||||
$handler->display->display_options['filters']['title']['id'] = 'title';
|
||||
$handler->display->display_options['filters']['title']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['title']['field'] = 'title';
|
||||
$handler->display->display_options['filters']['title']['operator'] = 'word';
|
||||
$handler->display->display_options['filters']['title']['group'] = 1;
|
||||
$handler->display->display_options['filters']['title']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['title']['expose']['operator_id'] = 'title_op';
|
||||
$handler->display->display_options['filters']['title']['expose']['label'] = 'Node title';
|
||||
$handler->display->display_options['filters']['title']['expose']['operator'] = 'title_op';
|
||||
$handler->display->display_options['filters']['title']['expose']['identifier'] = 'title';
|
||||
/* Filter criterion: Published */
|
||||
$handler->display->display_options['filters']['status']['id'] = 'status';
|
||||
$handler->display->display_options['filters']['status']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['status']['field'] = 'status';
|
||||
$handler->display->display_options['filters']['status']['ui_name'] = 'Published';
|
||||
$handler->display->display_options['filters']['status']['value'] = '1';
|
||||
$handler->display->display_options['filters']['status']['group'] = 1;
|
||||
$handler->display->display_options['filters']['status']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['status']['expose']['operator_id'] = '';
|
||||
$handler->display->display_options['filters']['status']['expose']['label'] = 'Published';
|
||||
$handler->display->display_options['filters']['status']['expose']['operator'] = 'status_op';
|
||||
$handler->display->display_options['filters']['status']['expose']['identifier'] = 'status';
|
||||
$handler->display->display_options['filters']['status']['expose']['required'] = TRUE;
|
||||
/* Filter criterion: Source translation */
|
||||
$handler->display->display_options['filters']['source_translation']['id'] = 'source_translation';
|
||||
$handler->display->display_options['filters']['source_translation']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['source_translation']['field'] = 'source_translation';
|
||||
$handler->display->display_options['filters']['source_translation']['ui_name'] = 'Source translation';
|
||||
$handler->display->display_options['filters']['source_translation']['operator'] = '1';
|
||||
$handler->display->display_options['filters']['source_translation']['group'] = 1;
|
||||
/* Filter criterion: Content: Language */
|
||||
$handler->display->display_options['filters']['language']['id'] = 'language';
|
||||
$handler->display->display_options['filters']['language']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['language']['field'] = 'language';
|
||||
$handler->display->display_options['filters']['language']['operator'] = 'not in';
|
||||
$handler->display->display_options['filters']['language']['value'] = array(
|
||||
'und' => 'und',
|
||||
);
|
||||
$handler->display->display_options['filters']['language']['group'] = 1;
|
||||
/* Filter criterion: Content: Language */
|
||||
$handler->display->display_options['filters']['language_1']['id'] = 'language_1';
|
||||
$handler->display->display_options['filters']['language_1']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['language_1']['field'] = 'language';
|
||||
$handler->display->display_options['filters']['language_1']['group'] = 1;
|
||||
$handler->display->display_options['filters']['language_1']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['language_1']['expose']['operator_id'] = 'language_1_op';
|
||||
$handler->display->display_options['filters']['language_1']['expose']['label'] = 'Source language';
|
||||
$handler->display->display_options['filters']['language_1']['expose']['operator'] = 'language_1_op';
|
||||
$handler->display->display_options['filters']['language_1']['expose']['identifier'] = 'language_1';
|
||||
/* Filter criterion: Content translation: Select translatable content types */
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['id'] = 'tmgmt_translatable_types_select';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['field'] = 'tmgmt_translatable_types_select';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['expose']['operator_id'] = 'tmgmt_translatable_types_select_op';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['expose']['label'] = 'Content type';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['expose']['operator'] = 'tmgmt_translatable_types_select_op';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['expose']['identifier'] = 'tmgmt_translatable_types_select';
|
||||
/* Filter criterion: Content translation: All translatable types */
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_all']['id'] = 'tmgmt_translatable_types_all';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_all']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_all']['field'] = 'tmgmt_translatable_types_all';
|
||||
/* Filter criterion: Content translation: Missing translation */
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['id'] = 'tmgmt_node_missing_translation';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['field'] = 'tmgmt_node_missing_translation';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['expose']['operator_id'] = 'tmgmt_node_missing_translation_op';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['expose']['label'] = 'Target language';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['expose']['operator'] = 'tmgmt_node_missing_translation_op';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['expose']['identifier'] = 'tmgmt_node_missing_translation';
|
||||
|
||||
/* Display: Page */
|
||||
$handler = $view->new_display('page', 'Page', 'page');
|
||||
$handler->display->display_options['path'] = 'admin/tmgmt/sources/node';
|
||||
$handler->display->display_options['menu']['type'] = 'tab';
|
||||
$handler->display->display_options['menu']['title'] = 'Content';
|
||||
$handler->display->display_options['menu']['weight'] = -20;
|
||||
$handler->display->display_options['menu']['context'] = 0;
|
||||
$translatables['tmgmt_node_source_overview'] = array(
|
||||
t('Master'),
|
||||
t('Content overview'),
|
||||
t('more'),
|
||||
t('Search'),
|
||||
t('Reset'),
|
||||
t('Sort by'),
|
||||
t('Asc'),
|
||||
t('Desc'),
|
||||
t('Items per page'),
|
||||
t('- All -'),
|
||||
t('Offset'),
|
||||
t('« first'),
|
||||
t('‹ previous'),
|
||||
t('next ›'),
|
||||
t('last »'),
|
||||
t('There are no nodes that match the specified filter criteria.'),
|
||||
t('User'),
|
||||
t('<!--views-bulk-operations-select-all-->'),
|
||||
t('Title (in source language)'),
|
||||
t('Type'),
|
||||
t('All translation languages'),
|
||||
t('Author'),
|
||||
t('Updated date'),
|
||||
t('Node title'),
|
||||
t('Published'),
|
||||
t('Source language'),
|
||||
t('Content type'),
|
||||
t('Page'),
|
||||
);
|
@@ -0,0 +1,6 @@
|
||||
<p>one paragraph with special characters: äöüľščťžýáíéäňú©«®™»</p>
|
||||
<p>one paragraph with a <br />break line</p>
|
||||
<p>one paragraph with html entities: &<></p>
|
||||
<p>and here we have some link <a href="http://example.com">break line</a></p>
|
||||
<p>one paragraph with an <img src="not-existing.gif" alt="not existing image" title="not existing image" />image</p>
|
||||
<p>hello <span class="green">world</span> this is <span class="red">simple html</span></p><div>nested 1<div>nested 2<div>nested 3</div></div></div>
|
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains tests for Translation management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility test case class with helper methods to create entities and their
|
||||
* fields with populated translatable content. Extend this class if you create
|
||||
* tests in which you need Drupal entities and/or fields.
|
||||
*/
|
||||
abstract class TMGMTEntityTestCaseUtility extends TMGMTBaseTestCase {
|
||||
|
||||
public $field_names = array();
|
||||
|
||||
/**
|
||||
* Creates node type with several text fields with different cardinality.
|
||||
*
|
||||
* Internally it calls TMGMTEntityTestCaseUtility::attachFields() to create
|
||||
* and attach fields to newly created bundle. You can than use
|
||||
* $this->field_names['node']['YOUR_BUNDLE_NAME'] to access them.
|
||||
*
|
||||
* @param string $machine_name
|
||||
* Machine name of the node type.
|
||||
* @param string $human_name
|
||||
* Human readable name of the node type.
|
||||
* @param int $language_content_type
|
||||
* Either 0 (disabled), 1 (language enabled but no translations),
|
||||
* TRANSLATION_ENABLED or ENTITY_TRANSLATION_ENABLED.
|
||||
* pparam bool $attach_fields
|
||||
* (optional) If fields with the same translatability should automatically
|
||||
* be attached to the node type.
|
||||
*/
|
||||
function createNodeType($machine_name, $human_name, $language_content_type = 0, $attach_fields = TRUE) {
|
||||
|
||||
// Create new bundle.
|
||||
$type = array(
|
||||
'type' => $machine_name,
|
||||
'name' => $human_name,
|
||||
'base' => 'node_content',
|
||||
'description' => '',
|
||||
'custom' => 1,
|
||||
'modified' => 1,
|
||||
'locked' => 0,
|
||||
);
|
||||
$type = node_type_set_defaults($type);
|
||||
node_type_save($type);
|
||||
node_add_body_field($type);
|
||||
node_types_rebuild();
|
||||
|
||||
// Set content type to be translatable as specified by
|
||||
// $language_content_type.
|
||||
$edit = array();
|
||||
$edit['language_content_type'] = $language_content_type;
|
||||
$this->drupalPost('admin/structure/types/manage/' . $machine_name, $edit, t('Save content type'));
|
||||
|
||||
$translatable = FALSE;
|
||||
if (defined('ENTITY_TRANSLATION_ENABLED') && $language_content_type == ENTITY_TRANSLATION_ENABLED) {
|
||||
$translatable = TRUE;
|
||||
}
|
||||
|
||||
// Push in also the body field.
|
||||
$this->field_names['node'][$machine_name][] = 'body';
|
||||
|
||||
if ($attach_fields) {
|
||||
$this->attachFields('node', $machine_name, $translatable);
|
||||
}
|
||||
|
||||
// Change body field to be translatable.
|
||||
$body = field_info_field('body');
|
||||
$body['translatable'] = $translatable;
|
||||
field_update_field($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates taxonomy vocabulary with custom fields.
|
||||
*
|
||||
* To create and attach fields it internally calls
|
||||
* TMGMTEntityTestCaseUtility::attachFields(). You can than access these
|
||||
* fields calling $this->field_names['node']['YOUR_BUNDLE_NAME'].
|
||||
*
|
||||
* @param string $machine_name
|
||||
* Vocabulary machine name.
|
||||
* @param string $human_name
|
||||
* Vocabulary human readable name.
|
||||
* @param bool|array $fields_translatable
|
||||
* Flag or definition array to determine which or all fields should be
|
||||
* translatable.
|
||||
*
|
||||
* @return stdClass
|
||||
* Created vocabulary object.
|
||||
*/
|
||||
function createTaxonomyVocab($machine_name, $human_name, $fields_translatable = TRUE) {
|
||||
$vocabulary = new stdClass();
|
||||
$vocabulary->name = $human_name;
|
||||
$vocabulary->machine_name = $machine_name;
|
||||
taxonomy_vocabulary_save($vocabulary);
|
||||
|
||||
$this->attachFields('taxonomy_term', $vocabulary->machine_name, $fields_translatable);
|
||||
|
||||
return $vocabulary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates fields of type text and text_with_summary of different cardinality.
|
||||
*
|
||||
* It will attach created fields to provided entity name and bundle.
|
||||
*
|
||||
* Field names will be stored in $this->field_names['entity']['bundle']
|
||||
* through which you can access them.
|
||||
*
|
||||
* @param string $entity_name
|
||||
* Entity name to which fields should be attached.
|
||||
* @param string $bundle
|
||||
* Bundle name to which fields should be attached.
|
||||
* @param bool|array $translatable
|
||||
* Flag or definition array to determine which or all fields should be
|
||||
* translatable.
|
||||
*/
|
||||
function attachFields($entity_name, $bundle, $translatable = TRUE) {
|
||||
// Create several text fields.
|
||||
$field_types = array('text', 'text_with_summary');
|
||||
|
||||
for ($i = 0 ; $i <= 5; $i++) {
|
||||
$field_type = $field_types[array_rand($field_types, 1)];
|
||||
$field_name = drupal_strtolower($this->randomName());
|
||||
|
||||
// Create a field.
|
||||
$field = array(
|
||||
'field_name' => $field_name,
|
||||
'type' => $field_type,
|
||||
'cardinality' => mt_rand(1, 5),
|
||||
'translatable' => is_array($translatable) && isset($translatable[$i]) ? $translatable[$i] : (boolean) $translatable,
|
||||
);
|
||||
field_create_field($field);
|
||||
|
||||
// Create an instance of the previously created field.
|
||||
$instance = array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => $entity_name,
|
||||
'bundle' => $bundle,
|
||||
'label' => $this->randomName(10),
|
||||
'description' => $this->randomString(30),
|
||||
'widget' => array(
|
||||
'type' => $field_type == 'text' ? 'text_textfield' : 'text_textarea_with_summary',
|
||||
'label' => $this->randomString(10),
|
||||
),
|
||||
);
|
||||
field_create_instance($instance);
|
||||
|
||||
// Store field names in case there are needed outside this method.
|
||||
$this->field_names[$entity_name][$bundle][] = $field_name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a node of a given bundle.
|
||||
*
|
||||
* It uses $this->field_names to populate content of attached fields.
|
||||
*
|
||||
* @param string $bundle
|
||||
* Node type name.
|
||||
* @param string $sourcelang
|
||||
* Source lang of the node to be created.
|
||||
*
|
||||
* @return object
|
||||
* Newly created node object.
|
||||
*/
|
||||
function createNode($bundle, $sourcelang = 'en') {
|
||||
$node = array(
|
||||
'type' => $bundle,
|
||||
'language' => $sourcelang,
|
||||
// Ensure that the body field is defined for the node language.
|
||||
'body' => array($sourcelang => array(0 => array())),
|
||||
);
|
||||
|
||||
foreach ($this->field_names['node'][$bundle] as $field_name) {
|
||||
$field_info = field_info_field($field_name);
|
||||
$cardinality = $field_info['cardinality'] == FIELD_CARDINALITY_UNLIMITED ? 1 : $field_info['cardinality'];
|
||||
$field_langcode = field_is_translatable('node', $field_info) ? $sourcelang : LANGUAGE_NONE;
|
||||
|
||||
// Create two deltas for each field.
|
||||
for ($delta = 0; $delta <= $cardinality; $delta++) {
|
||||
$node[$field_name][$field_langcode][$delta]['value'] = $this->randomName(20);
|
||||
if ($field_info['type'] == 'text_with_summary') {
|
||||
$node[$field_name][$field_langcode][$delta]['summary'] = $this->randomName(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->drupalCreateNode($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a taxonomy term of a given vocabulary.
|
||||
*
|
||||
* It uses $this->field_names to populate content of attached fields. You can
|
||||
* access fields values using
|
||||
* $this->field_names['taxonomy_term'][$vocabulary->machine_name].
|
||||
*
|
||||
* @param object $vocabulary
|
||||
* Vocabulary object for which the term should be created.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The language code to be set as the entity source language.
|
||||
*
|
||||
* @return object
|
||||
* Newly created node object.
|
||||
*/
|
||||
function createTaxonomyTerm($vocabulary, $langcode = 'en') {
|
||||
|
||||
// When an entity is being saved, the entity_translation module initializes
|
||||
// a translation fetching the language from an entity. But the taxonomy
|
||||
// terms have no entity language key, so its langcode will be the set to the
|
||||
// default one.
|
||||
/* @see entity_translation_field_attach_insert() */
|
||||
/* @see EntityTranslationDefaultHandler::initTranslations() */
|
||||
/* @see EntityTranslationDefaultHandler::getLanguage() */
|
||||
$settings_variable_name = 'entity_translation_settings_taxonomy_term__' . $vocabulary->machine_name;
|
||||
variable_set($settings_variable_name, array('default_language' => $langcode));
|
||||
|
||||
$term = new stdClass();
|
||||
$term->name = $this->randomName();
|
||||
$term->description = $this->randomName();
|
||||
$term->vid = $vocabulary->vid;
|
||||
|
||||
foreach ($this->field_names['taxonomy_term'][$vocabulary->machine_name] as $field_name) {
|
||||
$field_info = field_info_field($field_name);
|
||||
$cardinality = $field_info['cardinality'] == FIELD_CARDINALITY_UNLIMITED ? 1 : $field_info['cardinality'];
|
||||
$field_lang = $field_info['translatable'] ? $langcode : LANGUAGE_NONE;
|
||||
|
||||
// Create two deltas for each field.
|
||||
for ($delta = 0; $delta <= $cardinality; $delta++) {
|
||||
$term->{$field_name}[$field_lang][$delta]['value'] = $this->randomName(20);
|
||||
if ($field_info['type'] == 'text_with_summary') {
|
||||
$term->{$field_name}[$field_lang][$delta]['summary'] = $this->randomName(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
taxonomy_term_save($term);
|
||||
return taxonomy_term_load($term->tid);
|
||||
}
|
||||
}
|
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains tests for Translation management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for tests.
|
||||
*/
|
||||
class TMGMTBaseTestCase extends DrupalWebTestCase {
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* A default translator using the test translator.
|
||||
*
|
||||
* @var TMGMTTranslator
|
||||
*/
|
||||
protected $default_translator;
|
||||
|
||||
/**
|
||||
* List of permissions used by loginAsAdmin().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $admin_permissions = array();
|
||||
|
||||
/**
|
||||
* Drupal user object created by loginAsAdmin().
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $admin_user = NULL;
|
||||
|
||||
/**
|
||||
* List of permissions used by loginAsTranslator().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $translator_permissions = array();
|
||||
|
||||
/**
|
||||
* Drupal user object created by loginAsTranslator().
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $translator_user = NULL;
|
||||
|
||||
/**
|
||||
* Overrides DrupalWebTestCase::setUp()
|
||||
*/
|
||||
function setUp() {
|
||||
$modules = func_get_args();
|
||||
if (isset($modules[0]) && is_array($modules[0])) {
|
||||
$modules = $modules[0];
|
||||
}
|
||||
$modules = array_merge(array('entity', 'tmgmt', 'tmgmt_test'), $modules);
|
||||
parent::setUp($modules);
|
||||
$this->default_translator = tmgmt_translator_load('test_translator');
|
||||
|
||||
// Load default admin permissions.
|
||||
$this->admin_permissions = array(
|
||||
'administer languages',
|
||||
'access administration pages',
|
||||
'administer content types',
|
||||
'administer tmgmt',
|
||||
);
|
||||
|
||||
// Load default translator user permissions.
|
||||
$this->translator_permissions = array(
|
||||
'create translation jobs',
|
||||
'submit translation jobs',
|
||||
'accept translation jobs',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will create a user with admin permissions and log it in.
|
||||
*
|
||||
* @param array $additional_permissions
|
||||
* Additional permissions that will be granted to admin user.
|
||||
* @param boolean $reset_permissions
|
||||
* Flag to determine if default admin permissions will be replaced by
|
||||
* $additional_permissions.
|
||||
*
|
||||
* @return object
|
||||
* Newly created and logged in user object.
|
||||
*/
|
||||
function loginAsAdmin($additional_permissions = array(), $reset_permissions = FALSE) {
|
||||
$permissions = $this->admin_permissions;
|
||||
|
||||
if ($reset_permissions) {
|
||||
$permissions = $additional_permissions;
|
||||
}
|
||||
elseif (!empty($additional_permissions)) {
|
||||
$permissions = array_merge($permissions, $additional_permissions);
|
||||
}
|
||||
|
||||
$this->admin_user = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->admin_user);
|
||||
return $this->admin_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will create a user with translator permissions and log it in.
|
||||
*
|
||||
* @param array $additional_permissions
|
||||
* Additional permissions that will be granted to admin user.
|
||||
* @param boolean $reset_permissions
|
||||
* Flag to determine if default admin permissions will be replaced by
|
||||
* $additional_permissions.
|
||||
*
|
||||
* @return object
|
||||
* Newly created and logged in user object.
|
||||
*/
|
||||
function loginAsTranslator($additional_permissions = array(), $reset_permissions = FALSE) {
|
||||
$permissions = $this->translator_permissions;
|
||||
|
||||
if ($reset_permissions) {
|
||||
$permissions = $additional_permissions;
|
||||
}
|
||||
elseif (!empty($additional_permissions)) {
|
||||
$permissions = array_merge($permissions, $additional_permissions);
|
||||
}
|
||||
|
||||
$this->translator_user = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->translator_user);
|
||||
return $this->translator_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates, saves and returns a translator.
|
||||
*
|
||||
* @return TMGMTTranslator
|
||||
*/
|
||||
function createTranslator() {
|
||||
$translator = new TMGMTTranslator();
|
||||
$translator->name = strtolower($this->randomName());
|
||||
$translator->label = $this->randomName();
|
||||
$translator->plugin = 'test_translator';
|
||||
$translator->settings = array(
|
||||
'key' => $this->randomName(),
|
||||
'another_key' => $this->randomName(),
|
||||
);
|
||||
$this->assertEqual(SAVED_NEW, $translator->save());
|
||||
|
||||
// Assert that the translator was assigned a tid.
|
||||
$this->assertTrue($translator->tid > 0);
|
||||
return $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates, saves and returns a translation job.
|
||||
*
|
||||
* @return TMGMTJob
|
||||
*/
|
||||
function createJob($source = 'en', $target = 'de', $uid = 1) {
|
||||
$job = tmgmt_job_create($source, $target, $uid);
|
||||
$this->assertEqual(SAVED_NEW, $job->save());
|
||||
|
||||
// Assert that the translator was assigned a tid.
|
||||
$this->assertTrue($job->tjid > 0);
|
||||
return $job;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the proper environment.
|
||||
*
|
||||
* Currently just adds a new language.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The language code.
|
||||
*/
|
||||
function setEnvironment($langcode) {
|
||||
// Add the language.
|
||||
locale_add_language($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts job item language codes.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
* Job item to check.
|
||||
* @param string $expected_source_lang
|
||||
* Expected source language.
|
||||
* @param array $actual_lang_codes
|
||||
* Expected existing language codes (translations).
|
||||
*/
|
||||
function assertJobItemLangCodes(TMGMTJobItem $job_item, $expected_source_lang, array $actual_lang_codes) {
|
||||
$this->assertEqual($job_item->getSourceLangCode(), $expected_source_lang);
|
||||
$existing = $job_item->getExistingLangCodes();
|
||||
sort($existing);
|
||||
sort($actual_lang_codes);
|
||||
$this->assertEqual($existing, $actual_lang_codes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds languages as admin user and switches to a translator user.
|
||||
*/
|
||||
protected function createLanguagesLoginTranslator($permissions = NULL) {
|
||||
// Login as admin to be able to set environment variables.
|
||||
$this->loginAsAdmin();
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('es');
|
||||
$this->setEnvironment('el');
|
||||
|
||||
$base_permissions = array(
|
||||
'access administration pages',
|
||||
'create translation jobs',
|
||||
'submit translation jobs',
|
||||
);
|
||||
$permissions = $permissions ? array_merge($permissions, $base_permissions) : $base_permissions;
|
||||
// Login as translator only with limited permissions to run these tests.
|
||||
$this->loginAsTranslator($permissions, TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,532 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains tests for Translation management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic CRUD tests.
|
||||
*/
|
||||
class TMGMTCRUDTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'CRUD tests',
|
||||
'description' => 'Basic crud operations for jobs and translators',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test crud operations of translators.
|
||||
*/
|
||||
function testTranslators() {
|
||||
$translator = $this->createTranslator();
|
||||
|
||||
$loaded_translator = tmgmt_translator_load($translator->tid);
|
||||
|
||||
$this->assertEqual($translator->name, $loaded_translator->name);
|
||||
$this->assertEqual($translator->label, $loaded_translator->label);
|
||||
$this->assertEqual($translator->settings, $loaded_translator->settings);
|
||||
|
||||
// Update the settings.
|
||||
$translator->settings['new_key'] = $this->randomString();
|
||||
$this->assertEqual(SAVED_UPDATED, $translator->save());
|
||||
|
||||
$loaded_translator = tmgmt_translator_load($translator->tid);
|
||||
|
||||
$this->assertEqual($translator->name, $loaded_translator->name);
|
||||
$this->assertEqual($translator->label, $loaded_translator->label);
|
||||
$this->assertEqual($translator->settings, $loaded_translator->settings);
|
||||
|
||||
// Delete the translator, make sure the translator is gone.
|
||||
$translator->delete();
|
||||
$this->assertFalse(tmgmt_translator_load($translator->tid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test crud operations of jobs.
|
||||
*/
|
||||
function testJobs() {
|
||||
$job = $this->createJob();
|
||||
|
||||
$loaded_job = tmgmt_job_load($job->tjid);
|
||||
|
||||
$this->assertEqual($job->source_language, $loaded_job->source_language);
|
||||
$this->assertEqual($job->target_language, $loaded_job->target_language);
|
||||
|
||||
// Assert that the created and changed information has been set to the
|
||||
// default value.
|
||||
$this->assertTrue($loaded_job->created > 0);
|
||||
$this->assertTrue($loaded_job->changed > 0);
|
||||
$this->assertEqual(0, $loaded_job->state);
|
||||
|
||||
// Update the settings.
|
||||
$job->reference = 7;
|
||||
$this->assertEqual(SAVED_UPDATED, $job->save());
|
||||
|
||||
$loaded_job = tmgmt_job_load($job->tjid);
|
||||
|
||||
$this->assertEqual($job->reference, $loaded_job->reference);
|
||||
|
||||
// Test the job items.
|
||||
$item1 = $job->addItem('test_source', 'type', 5);
|
||||
$item2 = $job->addItem('test_source', 'type', 4);
|
||||
|
||||
// Load and compare the items.
|
||||
$items = $job->getItems();
|
||||
$this->assertEqual(2, count($items));
|
||||
|
||||
$this->assertEqual($item1->plugin, $items[$item1->tjiid]->plugin);
|
||||
$this->assertEqual($item1->item_type, $items[$item1->tjiid]->item_type);
|
||||
$this->assertEqual($item1->item_id, $items[$item1->tjiid]->item_id);
|
||||
$this->assertEqual($item2->plugin, $items[$item2->tjiid]->plugin);
|
||||
$this->assertEqual($item2->item_type, $items[$item2->tjiid]->item_type);
|
||||
$this->assertEqual($item2->item_id, $items[$item2->tjiid]->item_id);
|
||||
|
||||
// Delete the job and make sure it is gone.
|
||||
$job->delete();
|
||||
$this->assertFalse(tmgmt_job_load($job->tjid));
|
||||
}
|
||||
|
||||
function testRemoteMappings() {
|
||||
|
||||
$data_key = '5][test_source][type';
|
||||
|
||||
$translator = $this->createTranslator();
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->save();
|
||||
$item1 = $job->addItem('test_source', 'type', 5);
|
||||
$item2 = $job->addItem('test_source', 'type', 4);
|
||||
|
||||
$mapping_data = array(
|
||||
'remote_identifier_2' => 'id12',
|
||||
'remote_identifier_3' => 'id13',
|
||||
'amount' => 1043,
|
||||
'currency' => 'EUR',
|
||||
);
|
||||
|
||||
$result = $item1->addRemoteMapping($data_key, 'id11', $mapping_data);
|
||||
$this->assertEqual($result, SAVED_NEW);
|
||||
|
||||
$job_mappings = $job->getRemoteMappings();
|
||||
$item_mappings = $item1->getRemoteMappings();
|
||||
|
||||
$job_mapping = array_shift($job_mappings);
|
||||
$item_mapping = array_shift($item_mappings);
|
||||
|
||||
$_job = $job_mapping->getJob();
|
||||
$this->assertEqual($job->tjid, $_job->tjid);
|
||||
|
||||
$_job = $item_mapping->getJob();
|
||||
$this->assertEqual($job->tjid, $_job->tjid);
|
||||
|
||||
$_item1 = $item_mapping->getJobItem();
|
||||
$this->assertEqual($item1->tjiid, $_item1->tjiid);
|
||||
|
||||
/**
|
||||
* @var TMGMTRemoteController $remote_mapping_controller
|
||||
*/
|
||||
$remote_mapping_controller = entity_get_controller('tmgmt_remote');
|
||||
$remote_mappings = $remote_mapping_controller->loadByRemoteIdentifier('id11', 'id12', 'id13');
|
||||
$remote_mapping = array_shift($remote_mappings);
|
||||
$this->assertEqual($remote_mapping->tjiid, $item1->tjiid);
|
||||
$this->assertEqual($remote_mapping->amount, $mapping_data['amount']);
|
||||
$this->assertEqual($remote_mapping->currency, $mapping_data['currency']);
|
||||
|
||||
$this->assertEqual(count($remote_mapping_controller->loadByRemoteIdentifier('id11')), 1);
|
||||
$this->assertEqual(count($remote_mapping_controller->loadByRemoteIdentifier('id11', '')), 0);
|
||||
$this->assertEqual(count($remote_mapping_controller->loadByRemoteIdentifier('id11', NULL, '')), 0);
|
||||
$this->assertEqual(count($remote_mapping_controller->loadByRemoteIdentifier(NULL, NULL, 'id13')), 1);
|
||||
|
||||
// Test remote data.
|
||||
$item_mapping->addRemoteData('test_data', 'test_value');
|
||||
entity_save('tmgmt_remote', $item_mapping);
|
||||
$item_mapping = entity_load_single('tmgmt_remote', $item_mapping->trid);
|
||||
$this->assertEqual($item_mapping->getRemoteData('test_data'), 'test_value');
|
||||
|
||||
// Add mapping to the other job item as well.
|
||||
$item2->addRemoteMapping($data_key, 'id21', array('remote_identifier_2' => 'id22', 'remote_identifier_3' => 'id23'));
|
||||
|
||||
// Test deleting.
|
||||
|
||||
// Delete item1.
|
||||
entity_get_controller('tmgmt_job_item')->delete(array($item1->tjiid));
|
||||
// Test if mapping for item1 has been removed as well.
|
||||
|
||||
$this->assertEqual(count($remote_mapping_controller->loadByLocalData(NULL, $item1->tjiid)), 0);
|
||||
|
||||
// We still should have mapping for item2.
|
||||
$this->assertEqual(count($remote_mapping_controller->loadByLocalData(NULL, $item2->tjiid)), 1);
|
||||
|
||||
// Now delete the job and see if remaining mappings were removed as well.
|
||||
entity_get_controller('tmgmt_job')->delete(array($job->tjid));
|
||||
$this->assertEqual(count($remote_mapping_controller->loadByLocalData(NULL, $item2->tjiid)), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test crud operations of job items.
|
||||
*/
|
||||
function testJobItems() {
|
||||
$job = $this->createJob();
|
||||
|
||||
// Add some test items.
|
||||
$item1 = $job->addItem('test_source', 'type', 5);
|
||||
$item2 = $job->addItem('test_source', 'test_with_long_label', 4);
|
||||
|
||||
// Test single load callback.
|
||||
$item = tmgmt_job_item_load($item1->tjiid);
|
||||
$this->assertEqual($item1->plugin, $item->plugin);
|
||||
$this->assertEqual($item1->item_type, $item->item_type);
|
||||
$this->assertEqual($item1->item_id, $item->item_id);
|
||||
|
||||
// Test multiple load callback.
|
||||
$items = tmgmt_job_item_load_multiple(array($item1->tjiid, $item2->tjiid));
|
||||
|
||||
$this->assertEqual(2, count($items));
|
||||
|
||||
$this->assertEqual($item1->plugin, $items[$item1->tjiid]->plugin);
|
||||
$this->assertEqual($item1->item_type, $items[$item1->tjiid]->item_type);
|
||||
$this->assertEqual($item1->item_id, $items[$item1->tjiid]->item_id);
|
||||
$this->assertEqual($item2->plugin, $items[$item2->tjiid]->plugin);
|
||||
$this->assertEqual($item2->item_type, $items[$item2->tjiid]->item_type);
|
||||
$this->assertEqual($item2->item_id, $items[$item2->tjiid]->item_id);
|
||||
// Test the second item label length - it must not exceed the
|
||||
// TMGMT_JOB_LABEL_MAX_LENGTH.
|
||||
$this->assertTrue(TMGMT_JOB_LABEL_MAX_LENGTH >= strlen($items[$item2->tjiid]->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding translated data and revision handling.
|
||||
*/
|
||||
function testAddingTranslatedData() {
|
||||
$translator = $this->createTranslator();
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->save();
|
||||
|
||||
// Add some test items.
|
||||
$item1 = $job->addItem('test_source', 'test_with_long_label', 5);
|
||||
// Test the job label - it must not exceed the TMGMT_JOB_LABEL_MAX_LENGTH.
|
||||
$this->assertTrue(TMGMT_JOB_LABEL_MAX_LENGTH >= strlen($job->label()));
|
||||
|
||||
$key = array('dummy', 'deep_nesting');
|
||||
|
||||
$translation['dummy']['deep_nesting']['#text'] = 'translated 1';
|
||||
$item1->addTranslatedData($translation);
|
||||
$data = $item1->getData($key);
|
||||
|
||||
// Check job messages.
|
||||
$messages = $job->getMessages();
|
||||
$this->assertEqual(count($messages), 1);
|
||||
$last_message = end($messages);
|
||||
$this->assertEqual($last_message->message, 'The translation of !source to @language is finished and can now be <a href="!review_url">reviewed</a>.');
|
||||
|
||||
// Initial state - translation has been received for the first time.
|
||||
$this->assertEqual($data['#translation']['#text'], 'translated 1');
|
||||
$this->assertTrue(empty($data['#translation']['#text_revisions']));
|
||||
$this->assertEqual($data['#translation']['#origin'], 'remote');
|
||||
$this->assertEqual($data['#translation']['#timestamp'], REQUEST_TIME);
|
||||
|
||||
// Set status back to pending as if the data item was rejected.
|
||||
$item1->updateData(array('dummy', 'deep_nesting'), array('#status' => TMGMT_DATA_ITEM_STATE_PENDING));
|
||||
// Add same translation text.
|
||||
$translation['dummy']['deep_nesting']['#text'] = 'translated 1';
|
||||
$item1->addTranslatedData($translation);
|
||||
$data = $item1->getData($key);
|
||||
// Check if the status has been updated back to translated.
|
||||
$this->assertEqual($data['#status'], TMGMT_DATA_ITEM_STATE_TRANSLATED);
|
||||
|
||||
// Add translation, however locally customized.
|
||||
$translation['dummy']['deep_nesting']['#text'] = 'translated 2';
|
||||
$translation['dummy']['deep_nesting']['#origin'] = 'local';
|
||||
$translation['dummy']['deep_nesting']['#timestamp'] = REQUEST_TIME - 5;
|
||||
$item1->addTranslatedData($translation);
|
||||
$data = $item1->getData($key);
|
||||
|
||||
// The translation text is updated.
|
||||
$this->assertEqual($data['#translation']['#text'], 'translated 2');
|
||||
$this->assertEqual($data['#translation']['#timestamp'], REQUEST_TIME - 5);
|
||||
|
||||
// Previous translation is among text_revisions.
|
||||
$this->assertEqual($data['#translation']['#text_revisions'][0]['#text'], 'translated 1');
|
||||
$this->assertEqual($data['#translation']['#text_revisions'][0]['#origin'], 'remote');
|
||||
$this->assertEqual($data['#translation']['#text_revisions'][0]['#timestamp'], REQUEST_TIME);
|
||||
// Current translation origin is local.
|
||||
$this->assertEqual($data['#translation']['#origin'], 'local');
|
||||
|
||||
// Check job messages.
|
||||
$messages = $job->getMessages();
|
||||
$this->assertEqual(count($messages), 1);
|
||||
|
||||
// Add translation - not local.
|
||||
$translation['dummy']['deep_nesting']['#text'] = 'translated 3';
|
||||
unset($translation['dummy']['deep_nesting']['#origin']);
|
||||
unset($translation['dummy']['deep_nesting']['#timestamp']);
|
||||
$item1->addTranslatedData($translation);
|
||||
$data = $item1->getData($key);
|
||||
|
||||
// The translation text is NOT updated.
|
||||
$this->assertEqual($data['#translation']['#text'], 'translated 2');
|
||||
$this->assertEqual($data['#translation']['#timestamp'], REQUEST_TIME - 5);
|
||||
// Received translation is the latest revision.
|
||||
$last_revision = end($data['#translation']['#text_revisions']);
|
||||
$this->assertEqual($last_revision['#text'], 'translated 3');
|
||||
$this->assertEqual($last_revision['#origin'], 'remote');
|
||||
$this->assertEqual($last_revision['#timestamp'], REQUEST_TIME);
|
||||
// Current translation origin is local.
|
||||
$this->assertEqual($data['#translation']['#origin'], 'local');
|
||||
|
||||
// Check job messages.
|
||||
$messages = $job->getMessages();
|
||||
$this->assertEqual(count($messages), 2);
|
||||
$last_message = end($messages);
|
||||
$this->assertEqual($last_message->message, 'Translation for customized @key received. Revert your changes if you wish to use it.');
|
||||
|
||||
// Revert to previous revision which is the latest received translation.
|
||||
$item1->dataItemRevert($key);
|
||||
$data = $item1->getData($key);
|
||||
|
||||
// The translation text is updated.
|
||||
$this->assertEqual($data['#translation']['#text'], 'translated 3');
|
||||
$this->assertEqual($data['#translation']['#origin'], 'remote');
|
||||
$this->assertEqual($data['#translation']['#timestamp'], REQUEST_TIME);
|
||||
// Latest revision is now the formerly added local translation.
|
||||
$last_revision = end($data['#translation']['#text_revisions']);
|
||||
$this->assertTrue($last_revision['#text'], 'translated 2');
|
||||
$this->assertTrue($last_revision['#origin'], 'remote');
|
||||
$this->assertEqual($last_revision['#timestamp'], REQUEST_TIME - 5);
|
||||
|
||||
// Check job messages.
|
||||
$messages = $job->getMessages();
|
||||
$this->assertEqual(count($messages), 3);
|
||||
$last_message = end($messages);
|
||||
$this->assertEqual($last_message->message, 'Translation for @key reverted to the latest version.');
|
||||
|
||||
// There should be three revisions now.
|
||||
$this->assertEqual(count($data['#translation']['#text_revisions']), 3);
|
||||
|
||||
// Attempt to update the translation with the same text, this should not
|
||||
// lead to a new revision.
|
||||
$translation['dummy']['deep_nesting']['#text'] = 'translated 3';
|
||||
//unset($translation['dummy']['deep_nesting']['#origin']);
|
||||
//unset($translation['dummy']['deep_nesting']['#timestamp']);
|
||||
$item1->addTranslatedData($translation);
|
||||
$data = $item1->getData($key);
|
||||
$this->assertEqual(count($data['#translation']['#text_revisions']), 3);
|
||||
|
||||
// Mark the translation as reviewed, a new translation should not update the
|
||||
// existing one but create a new translation.
|
||||
$item1->updateData($key, array('#status' => TMGMT_DATA_ITEM_STATE_REVIEWED));
|
||||
$translation['dummy']['deep_nesting']['#text'] = 'translated 4';
|
||||
$item1->addTranslatedData($translation);
|
||||
$data = $item1->getData($key);
|
||||
|
||||
// The translation text is NOT updated.
|
||||
$this->assertEqual($data['#translation']['#text'], 'translated 3');
|
||||
// Received translation is the latest revision.
|
||||
$this->assertEqual(count($data['#translation']['#text_revisions']), 4);
|
||||
$last_revision = end($data['#translation']['#text_revisions']);
|
||||
$this->assertEqual($last_revision['#text'], 'translated 4');
|
||||
$this->assertEqual($last_revision['#origin'], 'remote');
|
||||
$this->assertEqual($last_revision['#timestamp'], REQUEST_TIME);
|
||||
|
||||
// Check job messages.
|
||||
$messages = $job->getMessages();
|
||||
$this->assertEqual(count($messages), 4);
|
||||
$last_message = end($messages);
|
||||
$this->assertEqual($last_message->message, 'Translation for already reviewed @key received and stored as a new revision. Revert to it if you wish to use it.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the calculations of the counters.
|
||||
*/
|
||||
function testJobItemsCounters() {
|
||||
$job = $this->createJob();
|
||||
|
||||
// Some test data items.
|
||||
$data1 = array(
|
||||
'#text' => 'The text to be translated.',
|
||||
);
|
||||
$data2 = array(
|
||||
'#text' => 'The text to be translated.',
|
||||
'#translation' => '',
|
||||
);
|
||||
$data3 = array(
|
||||
'#text' => 'The text to be translated.',
|
||||
'#translation' => 'The translated data. Set by the translator plugin.',
|
||||
);
|
||||
$data4 = array(
|
||||
'#text' => 'Another, longer text to be translated.',
|
||||
'#translation' => 'The translated data. Set by the translator plugin.',
|
||||
'#status' => TMGMT_DATA_ITEM_STATE_REVIEWED,
|
||||
);
|
||||
$data5 = array(
|
||||
'#label' => 'label',
|
||||
'data1' => $data1,
|
||||
'data4' => $data4,
|
||||
);
|
||||
|
||||
// No data items.
|
||||
$this->assertEqual(0, $job->getCountPending());
|
||||
$this->assertEqual(0, $job->getCountTranslated());
|
||||
$this->assertEqual(0, $job->getCountReviewed());
|
||||
$this->assertEqual(0, $job->getCountAccepted());
|
||||
$this->assertEqual(0, $job->getWordCount());
|
||||
|
||||
// Add a test items.
|
||||
$job_item1 = tmgmt_job_item_create('plugin', 'type', 4, array('tjid' => $job->tjid));
|
||||
$job_item1->save();
|
||||
|
||||
// No pending, translated and confirmed data items.
|
||||
$job = entity_load_single('tmgmt_job', $job->tjid);
|
||||
$job_item1 = entity_load_single('tmgmt_job_item', $job_item1->tjiid);
|
||||
drupal_static_reset('tmgmt_job_statistics_load');
|
||||
$this->assertEqual(0, $job_item1->getCountPending());
|
||||
$this->assertEqual(0, $job_item1->getCountTranslated());
|
||||
$this->assertEqual(0, $job_item1->getCountReviewed());
|
||||
$this->assertEqual(0, $job_item1->getCountAccepted());
|
||||
$this->assertEqual(0, $job->getCountPending());
|
||||
$this->assertEqual(0, $job->getCountTranslated());
|
||||
$this->assertEqual(0, $job->getCountReviewed());
|
||||
$this->assertEqual(0, $job->getCountAccepted());
|
||||
|
||||
// Add an untranslated data item.
|
||||
$job_item1->data['data_item1'] = $data1;
|
||||
$job_item1->save();
|
||||
|
||||
// One pending data items.
|
||||
$job = entity_load_single('tmgmt_job', $job->tjid);
|
||||
$job_item1 = entity_load_single('tmgmt_job_item', $job_item1->tjiid);
|
||||
drupal_static_reset('tmgmt_job_statistics_load');
|
||||
$this->assertEqual(1, $job_item1->getCountPending());
|
||||
$this->assertEqual(0, $job_item1->getCountTranslated());
|
||||
$this->assertEqual(0, $job_item1->getCountReviewed());
|
||||
$this->assertEqual(5, $job_item1->getWordCount());
|
||||
$this->assertEqual(1, $job->getCountPending());
|
||||
$this->assertEqual(0, $job->getCountReviewed());
|
||||
$this->assertEqual(0, $job->getCountTranslated());
|
||||
$this->assertEqual(5, $job->getWordCount());
|
||||
|
||||
|
||||
// Add another untranslated data item.
|
||||
// Test with an empty translation set.
|
||||
$job_item1->data['data_item1'] = $data2;
|
||||
$job_item1->save();
|
||||
|
||||
// One pending data items.
|
||||
$job = entity_load_single('tmgmt_job', $job->tjid);
|
||||
$job_item1 = entity_load_single('tmgmt_job_item', $job_item1->tjiid);
|
||||
drupal_static_reset('tmgmt_job_statistics_load');
|
||||
$this->assertEqual(1, $job_item1->getCountPending());
|
||||
$this->assertEqual(0, $job_item1->getCountTranslated());
|
||||
$this->assertEqual(0, $job_item1->getCountReviewed());
|
||||
$this->assertEqual(5, $job_item1->getWordCount());
|
||||
$this->assertEqual(1, $job->getCountPending());
|
||||
$this->assertEqual(0, $job->getCountTranslated());
|
||||
$this->assertEqual(0, $job->getCountReviewed());
|
||||
$this->assertEqual(5, $job->getWordCount());
|
||||
|
||||
// Add a translated data item.
|
||||
$job_item1->data['data_item1'] = $data3;
|
||||
$job_item1->save();
|
||||
|
||||
// One translated data items.
|
||||
drupal_static_reset('tmgmt_job_statistics_load');
|
||||
$this->assertEqual(0, $job_item1->getCountPending());
|
||||
$this->assertEqual(1, $job_item1->getCountTranslated());
|
||||
$this->assertEqual(0, $job_item1->getCountReviewed());
|
||||
$this->assertEqual(0, $job->getCountPending());
|
||||
$this->assertEqual(0, $job->getCountReviewed());
|
||||
$this->assertEqual(1, $job->getCountTranslated());
|
||||
|
||||
// Add a confirmed data item.
|
||||
$job_item1->data['data_item1'] = $data4;
|
||||
$job_item1->save();
|
||||
|
||||
// One reviewed data item.
|
||||
drupal_static_reset('tmgmt_job_statistics_load');
|
||||
$this->assertEqual(1, $job_item1->getCountReviewed());
|
||||
$this->assertEqual(1, $job->getCountReviewed());
|
||||
|
||||
// Add a translated and an untranslated and a confirmed data item
|
||||
$job = entity_load_single('tmgmt_job', $job->tjid);
|
||||
$job_item1 = entity_load_single('tmgmt_job_item', $job_item1->tjiid);
|
||||
$job_item1->data['data_item1'] = $data1;
|
||||
$job_item1->data['data_item2'] = $data3;
|
||||
$job_item1->data['data_item3'] = $data4;
|
||||
$job_item1->save();
|
||||
|
||||
// One pending and translated data items each.
|
||||
drupal_static_reset('tmgmt_job_statistics_load');
|
||||
$this->assertEqual(1, $job->getCountPending());
|
||||
$this->assertEqual(1, $job->getCountTranslated());
|
||||
$this->assertEqual(1, $job->getCountReviewed());
|
||||
$this->assertEqual(16, $job->getWordCount());
|
||||
|
||||
// Add nested data items.
|
||||
$job_item1->data['data_item1'] = $data5;
|
||||
$job_item1->save();
|
||||
|
||||
// One pending data items.
|
||||
$job = entity_load_single('tmgmt_job', $job->tjid);
|
||||
$job_item1 = entity_load_single('tmgmt_job_item', $job_item1->tjiid);
|
||||
$this->assertEqual('label', $job_item1->data['data_item1']['#label']);
|
||||
$this->assertEqual(3, count($job_item1->data['data_item1']));
|
||||
|
||||
// Add a greater number of data items
|
||||
for ($index = 1; $index <= 3; $index++) {
|
||||
$job_item1->data['data_item' . $index] = $data1;
|
||||
}
|
||||
for ($index = 4; $index <= 10; $index++) {
|
||||
$job_item1->data['data_item' . $index] = $data3;
|
||||
}
|
||||
for ($index = 11; $index <= 15; $index++) {
|
||||
$job_item1->data['data_item' . $index] = $data4;
|
||||
}
|
||||
$job_item1->save();
|
||||
|
||||
// 3 pending and 7 translated data items each.
|
||||
$job = entity_load_single('tmgmt_job', $job->tjid);
|
||||
drupal_static_reset('tmgmt_job_statistics_load');
|
||||
$this->assertEqual(3, $job->getCountPending());
|
||||
$this->assertEqual(7, $job->getCountTranslated());
|
||||
$this->assertEqual(5, $job->getCountReviewed());
|
||||
|
||||
// Add several job items
|
||||
$job_item2 = tmgmt_job_item_create('plugin', 'type', 5, array('tjid' => $job->tjid));
|
||||
for ($index = 1; $index <= 4; $index++) {
|
||||
$job_item2->data['data_item' . $index] = $data1;
|
||||
}
|
||||
for ($index = 5; $index <= 12; $index++) {
|
||||
$job_item2->data['data_item' . $index] = $data3;
|
||||
}
|
||||
for ($index = 13; $index <= 16; $index++) {
|
||||
$job_item2->data['data_item' . $index] = $data4;
|
||||
}
|
||||
$job_item2->save();
|
||||
|
||||
// 3 pending and 7 translated data items each.
|
||||
$job = entity_load_single('tmgmt_job', $job->tjid);
|
||||
drupal_static_reset('tmgmt_job_statistics_load');
|
||||
$this->assertEqual(7, $job->getCountPending());
|
||||
$this->assertEqual(15, $job->getCountTranslated());
|
||||
$this->assertEqual(9, $job->getCountReviewed());
|
||||
|
||||
// Accept the job items.
|
||||
foreach ($job->getItems() as $item) {
|
||||
// Set the state directly to avoid triggering translator and source
|
||||
// controllers that do not exist.
|
||||
$item->setState(TMGMT_JOB_ITEM_STATE_ACCEPTED);
|
||||
$item->save();
|
||||
}
|
||||
drupal_static_reset('tmgmt_job_statistics_load');
|
||||
$this->assertEqual(0, $job->getCountPending());
|
||||
$this->assertEqual(0, $job->getCountTranslated());
|
||||
$this->assertEqual(0, $job->getCountReviewed());
|
||||
$this->assertEqual(31, $job->getCountAccepted());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains tests for Translation management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test the helper functions in tmgmt.module.
|
||||
*/
|
||||
class TMGMTHelperTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Helper functions Test case',
|
||||
'description' => 'Helper functions for other modules',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests tmgmt_job_match_item()
|
||||
*
|
||||
* @see tmgmt_job_match_item
|
||||
*/
|
||||
function testTMGTJobMatchItem() {
|
||||
$this->loginAsAdmin();
|
||||
$this->setEnvironment('fr');
|
||||
$this->setEnvironment('es');
|
||||
|
||||
// Add a job from en to fr and en to sp.
|
||||
$job_en_fr = $this->createJob('en', 'fr');
|
||||
$job_en_sp = $this->createJob('en', 'es');
|
||||
|
||||
// Add a job which has existing source-target combinations.
|
||||
$this->assertEqual($job_en_fr->tjid, tmgmt_job_match_item('en', 'fr')->tjid);
|
||||
$this->assertEqual($job_en_sp->tjid, tmgmt_job_match_item('en', 'es')->tjid);
|
||||
|
||||
// Add a job which has no existing source-target combination.
|
||||
$this->assertTrue(tmgmt_job_match_item('fr', 'es'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the tmgmt_data_item_label() function.
|
||||
*
|
||||
* @todo: Move into a unit test case once available.
|
||||
*/
|
||||
function testDataIemLabel() {
|
||||
$no_label = array(
|
||||
'#text' => 'No label',
|
||||
);
|
||||
$this->assertEqual(tmgmt_data_item_label($no_label), 'No label');
|
||||
$this->assertEqual(tmgmt_data_item_label($no_label, 6), 'No ...');
|
||||
$label = array(
|
||||
'#parent_label' => array(),
|
||||
'#label' => 'A label',
|
||||
);
|
||||
$this->assertEqual(tmgmt_data_item_label($label), 'A label');
|
||||
$this->assertEqual(tmgmt_data_item_label($label, 6), 'A l...');
|
||||
$parent_label = array(
|
||||
'#parent_label' => array('Parent label', 'Sub label'),
|
||||
'#label' => 'A label',
|
||||
);
|
||||
$this->assertEqual(tmgmt_data_item_label($parent_label), 'Parent label > Sub label');
|
||||
$this->assertEqual(tmgmt_data_item_label($parent_label, 18), 'Pare... > Sub ...');
|
||||
$nested = array(
|
||||
'#parent_label' => array('Parent label', 'Sub label', 'Sub-sub label'),
|
||||
'#label' => 'A label',
|
||||
);
|
||||
$this->assertEqual(tmgmt_data_item_label($nested), 'Parent label > Sub label > Sub-sub label');
|
||||
$this->assertEqual(tmgmt_data_item_label($nested, 28), 'Pare... > Sub ... > Sub-...');
|
||||
$long_label = array(
|
||||
'#parent_label' => array('Loooooooooooong label', 'Short'),
|
||||
'#label' => 'A label',
|
||||
);
|
||||
$this->assertEqual(tmgmt_data_item_label($long_label), 'Loooooooooooong label > Short');
|
||||
$this->assertEqual(tmgmt_data_item_label($long_label, 30), 'Loooooooooooong label > Short');
|
||||
$node_example = array(
|
||||
'#parent_label' => array('This is a very loooong title, so looong', 'Body', 'Delta #0', 'Body'),
|
||||
'#label' => 'A label',
|
||||
);
|
||||
$this->assertEqual(tmgmt_data_item_label($node_example), 'This is a very loooong title, so looong > Body > Delta #0 > Body');
|
||||
$this->assertEqual(tmgmt_data_item_label($node_example, 56), 'This is a very loooong title... > Body > Delta #0 > Body');
|
||||
}
|
||||
|
||||
function testWordCount() {
|
||||
$unit_tests = array(
|
||||
'empty' => array(
|
||||
'text' => '',
|
||||
'count' => 0,
|
||||
),
|
||||
'latin' => array(
|
||||
'text' => 'Drupal is the best!',
|
||||
'count' => 4,
|
||||
),
|
||||
'non-latin' => array(
|
||||
'text' => 'Друпал лучший!',
|
||||
'count' => 2,
|
||||
),
|
||||
'complex punctuation' => array(
|
||||
'text' => '<[({-!ReAd@*;: ,?+MoRe...})]>\\|/',
|
||||
'count' => 2,
|
||||
'exclude_tags' => FALSE,
|
||||
),
|
||||
'repeat' => array(
|
||||
'text' => 'repeat repeat',
|
||||
'count' => 2,
|
||||
),
|
||||
'strip tags' => array(
|
||||
'text' => '<a href="http://example.com">link text</a> plain text <div class="some-css-class"></div>',
|
||||
'count' => 4,
|
||||
),
|
||||
);
|
||||
foreach ($unit_tests as $id => $test_data) {
|
||||
// Set the exclude_tags flag. In case not provided the TRUE is default.
|
||||
$test_data += array('exclude_tags' => TRUE);
|
||||
if (variable_get('tmgmt_word_count_exclude_tags', TRUE) != $test_data['exclude_tags']) {
|
||||
variable_set('tmgmt_word_count_exclude_tags', $test_data['exclude_tags']);
|
||||
}
|
||||
$this->assertEqual($real_count = tmgmt_word_count($test_data['text']), $desirable_count = $test_data['count'], t('!test_id: Real count (=!real_count) should be equal to desirable (=!desirable_count)', array('!test_id' => $id, '!real_count' => $real_count, '!desirable_count' => $desirable_count)));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains tests for Translation management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests interaction between core and the plugins.
|
||||
*/
|
||||
class TMGMTPluginsTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Plugin tests',
|
||||
'description' => 'Verifies basic functionality of source and translator plugins',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function createJob($source = 'en', $target = 'de', $uid = 1) {
|
||||
$job = parent::createJob();
|
||||
|
||||
for ($i = 1; $i < 3; $i++) {
|
||||
if ($i == 3) {
|
||||
// Explicitly define the data for the third item.
|
||||
$data['data'] = array(
|
||||
'dummy' => array(
|
||||
'deep_nesting' => array(
|
||||
'#text' => 'Stored data',
|
||||
),
|
||||
),
|
||||
);
|
||||
$job->addItem('test_source', 'test', $i, array($data));
|
||||
}
|
||||
$job->addItem('test_source', 'test', $i);
|
||||
}
|
||||
|
||||
// Manually specify the translator for now.
|
||||
$job->translator = $this->default_translator->name;
|
||||
|
||||
return $job;
|
||||
}
|
||||
|
||||
function testBasicWorkflow() {
|
||||
// Submit a translation job.
|
||||
$submit_job = $this->createJob();
|
||||
$submit_job->settings = array('action' => 'submit');
|
||||
$submit_job->requestTranslation();
|
||||
$submit_job = tmgmt_job_load($submit_job->tjid);
|
||||
$this->assertTrue($submit_job->isActive());
|
||||
$messages = $submit_job->getMessages();
|
||||
$last_message = end($messages);
|
||||
$this->assertEqual('Test submit.', $last_message->message);
|
||||
|
||||
// Translate a job.
|
||||
$translate_job = $this->createJob();
|
||||
$translate_job->settings = array('action' => 'translate');
|
||||
$translate_job->requestTranslation();
|
||||
$translate_job = tmgmt_job_load($translate_job->tjid);
|
||||
foreach ($translate_job->getItems() as $job_item) {
|
||||
$this->assertTrue($job_item->isNeedsReview());
|
||||
}
|
||||
|
||||
$messages = $translate_job->getMessages();
|
||||
// array_values() results in numeric keys, which is necessary for list.
|
||||
list($debug, $translated, $needs_review) = array_values($messages);
|
||||
$this->assertEqual('Test translator called.', $debug->message);
|
||||
$this->assertEqual('debug', $debug->type);
|
||||
$this->assertEqual('Test translation created.', $translated->message);
|
||||
$this->assertEqual('status', $translated->type);
|
||||
|
||||
// The third message is specific to a job item and has different state
|
||||
// constants.
|
||||
$this->assertEqual('The translation of !source to @language is finished and can now be <a href="!review_url">reviewed</a>.', $needs_review->message);
|
||||
$this->assertEqual('status', $needs_review->type);
|
||||
|
||||
$i = 1;
|
||||
foreach ($translate_job->getItems() as $item) {
|
||||
// Check the translated text.
|
||||
if ($i != 3) {
|
||||
$expected_text = 'de_Text for job item with type ' . $item->item_type . ' and id ' . $item->item_id . '.';
|
||||
}
|
||||
else {
|
||||
// The third item has an explicitly stored data value.
|
||||
$expected_text = 'de_Stored data';
|
||||
}
|
||||
$item_data = $item->getData();
|
||||
$this->assertEqual($expected_text, $item_data['dummy']['deep_nesting']['#translation']['#text']);
|
||||
$i++;
|
||||
}
|
||||
|
||||
foreach ($translate_job->getItems() as $job_item) {
|
||||
$job_item->acceptTranslation();
|
||||
}
|
||||
|
||||
// @todo Accepting does not result in messages on the job anymore.
|
||||
// Update once there are job item messages.
|
||||
/*
|
||||
$messages = $translate_job->getMessages();
|
||||
$last_message = end($messages);
|
||||
$this->assertEqual('Job accepted', $last_message->message);
|
||||
$this->assertEqual('status', $last_message->type);*/
|
||||
|
||||
// Check if the translations have been "saved".
|
||||
foreach ($translate_job->getItems() as $item) {
|
||||
$this->assertTrue(variable_get('tmgmt_test_saved_translation_' . $item->item_type . '_' . $item->item_id, FALSE));
|
||||
}
|
||||
|
||||
// A rejected job.
|
||||
$reject_job = $this->createJob();
|
||||
$reject_job->settings = array('action' => 'reject');
|
||||
$reject_job->requestTranslation();
|
||||
// Still rejected.
|
||||
$this->assertTrue($reject_job->isRejected());
|
||||
|
||||
$messages = $reject_job->getMessages();
|
||||
$last_message = end($messages);
|
||||
$this->assertEqual('This is not supported.', $last_message->message);
|
||||
$this->assertEqual('error', $last_message->type);
|
||||
|
||||
// A failing job.
|
||||
$failing_job = $this->createJob();
|
||||
$failing_job->settings = array('action' => 'fail');
|
||||
$failing_job->requestTranslation();
|
||||
// Still new.
|
||||
$this->assertTrue($failing_job->isUnprocessed());
|
||||
|
||||
$messages = $failing_job->getMessages();
|
||||
$last_message = end($messages);
|
||||
$this->assertEqual('Service not reachable.', $last_message->message);
|
||||
$this->assertEqual('error', $last_message->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests remote languages mappings support in the tmgmt core.
|
||||
*/
|
||||
function testRemoteLanguagesMappings() {
|
||||
$this->loginAsAdmin();
|
||||
$this->setEnvironment('de');
|
||||
$controller = $this->default_translator->getController();
|
||||
|
||||
$mappings = $controller->getRemoteLanguagesMappings($this->default_translator);
|
||||
$this->assertEqual($mappings, array(
|
||||
'en' => 'en-us',
|
||||
'de' => 'de-ch',
|
||||
));
|
||||
|
||||
$this->assertEqual($controller->mapToRemoteLanguage($this->default_translator, 'en'), 'en-us');
|
||||
$this->assertEqual($controller->mapToRemoteLanguage($this->default_translator, 'de'), 'de-ch');
|
||||
$this->assertEqual($controller->mapToLocalLanguage($this->default_translator, 'en-us'), 'en');
|
||||
$this->assertEqual($controller->mapToLocalLanguage($this->default_translator, 'de-ch'), 'de');
|
||||
|
||||
$this->default_translator->settings['remote_languages_mappings']['de'] = 'de-de';
|
||||
$this->default_translator->settings['remote_languages_mappings']['en'] = 'en-uk';
|
||||
$this->default_translator->save();
|
||||
|
||||
$this->assertEqual($controller->mapToRemoteLanguage($this->default_translator, 'en'), 'en-uk');
|
||||
$this->assertEqual($controller->mapToRemoteLanguage($this->default_translator, 'de'), 'de-de');
|
||||
|
||||
// Test the fallback.
|
||||
$info = &drupal_static('_tmgmt_plugin_info');
|
||||
$info['translator']['test_translator']['map remote languages'] = FALSE;
|
||||
|
||||
$this->assertEqual($controller->mapToRemoteLanguage($this->default_translator, 'en'), 'en');
|
||||
$this->assertEqual($controller->mapToRemoteLanguage($this->default_translator, 'de'), 'de');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests escaping and unescaping text.
|
||||
*/
|
||||
function testEscaping() {
|
||||
$controller = $this->default_translator->getController();
|
||||
|
||||
$tests = array(
|
||||
array(
|
||||
'item' => array('#text' => 'no escaping'),
|
||||
'expected' => 'no escaping',
|
||||
),
|
||||
array(
|
||||
'item' => array(
|
||||
'#text' => 'single placeholder',
|
||||
'#escape' => array(
|
||||
7 => array('string' => 'placeholder'),
|
||||
),
|
||||
),
|
||||
'expected' => 'single [[[placeholder]]]',
|
||||
),
|
||||
array(
|
||||
'item' => array(
|
||||
'#text' => 'two placeholder, the second placeholder',
|
||||
'#escape' => array(
|
||||
4 => array('string' => 'placeholder'),
|
||||
28 => array('string' => 'placeholder'),
|
||||
),
|
||||
),
|
||||
'expected' => 'two [[[placeholder]]], the second [[[placeholder]]]',
|
||||
),
|
||||
array(
|
||||
'item' => array(
|
||||
'#text' => 'something, something else',
|
||||
'#escape' => array(
|
||||
0 => array('string' => 'something'),
|
||||
21 => array('string' => 'else'),
|
||||
),
|
||||
),
|
||||
'expected' => '[[[something]]], something [[[else]]]',
|
||||
),
|
||||
array(
|
||||
'item' => array(
|
||||
'#text' => 'something, something else',
|
||||
'#escape' => array(
|
||||
21 => array('string' => 'else'),
|
||||
11 => array('string' => 'something'),
|
||||
),
|
||||
),
|
||||
'expected' => 'something, [[[something]]] [[[else]]]',
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($tests as $test) {
|
||||
$escaped = $controller->escapeText($test['item']);
|
||||
// Assert that the string was escaped as expected.
|
||||
$this->assertEqual($escaped, $test['expected']);
|
||||
|
||||
// Assert that the string is the same as the original when unescaped.
|
||||
$this->assertEqual($controller->unescapeText($escaped), $test['item']['#text']);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains tests for Translation management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Upgrade tests.
|
||||
*/
|
||||
class TMGMTUpgradeAlpha1TestCase extends DrupalWebTestCase {
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => t('Upgrade tests Alpha1'),
|
||||
'description' => t('Tests the upgrade path from 7.x-1.0-alpha1'),
|
||||
'group' => t('Translation Management'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
// Enable all dependencies.
|
||||
parent::setUp(array('entity', 'views', 'translation', 'locale'));
|
||||
|
||||
// Create the tmgmt tables and fill them.
|
||||
module_load_include('inc', 'tmgmt', 'tests/tmgmt_alpha1_dump.sql');
|
||||
|
||||
// @todo: Figure out why this is necessary.
|
||||
$enabled_modules = db_query("SELECT name FROM {system} where status = 1 and type = 'module'")->fetchCol();
|
||||
foreach ($enabled_modules as $enabled_module) {
|
||||
module_load_install($enabled_module);
|
||||
// Set the schema version to the number of the last update provided
|
||||
// by the module.
|
||||
$versions = drupal_get_schema_versions($enabled_module);
|
||||
$version = $versions ? max($versions) : SCHEMA_INSTALLED;
|
||||
db_update('system')
|
||||
->condition('name', $enabled_module)
|
||||
->fields(array('schema_version' => $version))
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Set schema version to 0 and then install the tmgmt modules, to simulate
|
||||
// an enabling.
|
||||
db_update('system')
|
||||
->condition('name', array('tmgmt', 'tmgmt_ui', 'tmgmt_field', 'tmgmt_node', 'tmgmt_test', 'tmgmt_node_ui'))
|
||||
->fields(array(
|
||||
'schema_version' => 0,
|
||||
))
|
||||
->execute();
|
||||
module_enable(array('tmgmt', 'tmgmt_ui', 'tmgmt_field', 'tmgmt_node', 'tmgmt_test', 'tmgmt_node_ui'));
|
||||
|
||||
// Log in as a user that can run update.php
|
||||
$admin = $this->drupalCreateUser(array('administer software updates'));
|
||||
$this->drupalLogin($admin);
|
||||
|
||||
$this->performUpgrade();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the data has been migrated properly
|
||||
*/
|
||||
function testUpgradePath() {
|
||||
// Log in as a user with enough permissions.
|
||||
$translator = $this->drupalCreateUser(array('administer tmgmt'));
|
||||
$this->drupalLogin($translator);
|
||||
// Go to a job and check the review form.
|
||||
$this->drupalGet('admin/tmgmt/jobs/1');
|
||||
// Make sure the #status values have been set accordingly.
|
||||
$this->assertRaw(t('Accepted: @accepted, reviewed: @reviewed, translated: @translated, pending: @pending.', array('@accepted' => 0, '@reviewed' => 0, '@translated' => 2, '@pending' => 0)));
|
||||
// Extract the word count field and make sure it's correct.
|
||||
$word_count = $this->xpath('//td[contains(@class, :class)]', array(':class' => 'views-field-word-count-1'));
|
||||
$this->assertEqual(6, trim((string)reset($word_count)));
|
||||
|
||||
$this->clickLink(t('review'));
|
||||
// Needs review icon.
|
||||
$this->assertRaw('tmgmt-ui-icon-yellow tmgmt-ui-state-translated');
|
||||
// Translated values.
|
||||
$this->assertRaw('de_Test content');
|
||||
$this->assertRaw('de_This is the body.');
|
||||
// Reject button.
|
||||
$this->assertRaw('✗');
|
||||
|
||||
// Check that accepted count has been updated correctly.
|
||||
$this->drupalGet('admin/tmgmt/jobs/2');
|
||||
// Make sure the #status values have been set accordingly.
|
||||
$this->assertRaw(t('Accepted: @accepted, reviewed: @reviewed, translated: @translated, pending: @pending.', array('@accepted' => 2, '@reviewed' => 0, '@translated' => 0, '@pending' => 0)));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform the upgrade.
|
||||
*
|
||||
* Copied and adapted from UpgradePathTestCase::performUpgrade().
|
||||
*
|
||||
* @param $register_errors
|
||||
* Register the errors during the upgrade process as failures.
|
||||
* @return
|
||||
* TRUE if the upgrade succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function performUpgrade($register_errors = TRUE) {
|
||||
$update_url = $GLOBALS['base_url'] . '/update.php';
|
||||
|
||||
// Load the first update screen.
|
||||
$this->drupalGet($update_url, array('external' => TRUE));
|
||||
if (!$this->assertResponse(200)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Continue.
|
||||
$this->drupalPost(NULL, array(), t('Continue'));
|
||||
if (!$this->assertResponse(200)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// The test should pass if there are no pending updates.
|
||||
$content = $this->drupalGetContent();
|
||||
if (strpos($content, t('No pending updates.')) !== FALSE) {
|
||||
$this->pass(t('No pending updates and therefore no upgrade process to test.'));
|
||||
$this->pendingUpdates = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Go!
|
||||
$this->drupalPost(NULL, array(), t('Apply pending updates'));
|
||||
if (!$this->assertResponse(200)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check for errors during the update process.
|
||||
foreach ($this->xpath('//li[@class=:class]', array(':class' => 'failure')) as $element) {
|
||||
$message = strip_tags($element->asXML());
|
||||
$this->upgradeErrors[] = $message;
|
||||
if ($register_errors) {
|
||||
$this->fail($message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->upgradeErrors)) {
|
||||
// Upgrade failed, the installation might be in an inconsistent state,
|
||||
// don't process.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check if there still are pending updates.
|
||||
$this->drupalGet($update_url, array('external' => TRUE));
|
||||
$this->drupalPost(NULL, array(), t('Continue'));
|
||||
if (!$this->assertText(t('No pending updates.'), t('No pending updates at the end of the update process.'))) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Clear caches.
|
||||
$this->checkPermissions(array(), TRUE);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,463 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Filled installation of TMGMT 7.x-1.0-alpha1, for test purposes.
|
||||
*
|
||||
* This file was generated by the dump-database-d7.sh tool, from an
|
||||
* installation of Drupal 7, filled with data using the generate-d7-content.sh
|
||||
* tool. It has the following modules installed:
|
||||
* - tmgmt
|
||||
* - tmgmt_field
|
||||
* - tmgmt_node
|
||||
* - tmgmt_node_ui
|
||||
* - tmgmt_test
|
||||
* - tmgmt_ui
|
||||
*/
|
||||
|
||||
db_create_table('cache_tmgmt', array(
|
||||
'fields' => array(
|
||||
'cid' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'data' => array(
|
||||
'type' => 'blob',
|
||||
'not null' => FALSE,
|
||||
'size' => 'big',
|
||||
),
|
||||
'expire' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'created' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'serialized' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'small',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'expire' => array(
|
||||
'expire',
|
||||
),
|
||||
),
|
||||
'primary key' => array(
|
||||
'cid',
|
||||
),
|
||||
'module' => 'tmgmt',
|
||||
'name' => 'cache_tmgmt',
|
||||
));
|
||||
|
||||
db_create_table('tmgmt_job', array(
|
||||
'fields' => array(
|
||||
'tjid' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'source_language' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 12,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'target_language' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 12,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'state' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'created' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'changed' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'translator' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
),
|
||||
'settings' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'reference' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 256,
|
||||
),
|
||||
'uid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'label' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 256,
|
||||
),
|
||||
),
|
||||
'primary key' => array(
|
||||
'tjid',
|
||||
),
|
||||
'indexes' => array(
|
||||
'state' => array(
|
||||
'state',
|
||||
),
|
||||
'reference' => array(
|
||||
'reference',
|
||||
),
|
||||
),
|
||||
'module' => 'tmgmt',
|
||||
'name' => 'tmgmt_job',
|
||||
));
|
||||
db_insert('tmgmt_job')->fields(array(
|
||||
'tjid',
|
||||
'source_language',
|
||||
'target_language',
|
||||
'state',
|
||||
'created',
|
||||
'changed',
|
||||
'translator',
|
||||
'settings',
|
||||
'reference',
|
||||
'uid',
|
||||
'label',
|
||||
))
|
||||
->values(array(
|
||||
'tjid' => '1',
|
||||
'source_language' => 'en',
|
||||
'target_language' => 'de',
|
||||
'state' => '1',
|
||||
'created' => '1342074121',
|
||||
'changed' => '1342074125',
|
||||
'translator' => 'test_translator',
|
||||
'settings' => 'a:1:{s:6:"action";s:9:"translate";}',
|
||||
'reference' => NULL,
|
||||
'uid' => '1',
|
||||
'label' => '',
|
||||
))
|
||||
->values(array(
|
||||
'tjid' => '2',
|
||||
'source_language' => 'en',
|
||||
'target_language' => 'es',
|
||||
'state' => '5',
|
||||
'created' => '1342074121',
|
||||
'changed' => '1342074127',
|
||||
'translator' => 'test_translator',
|
||||
'settings' => 'a:1:{s:6:"action";s:9:"translate";}',
|
||||
'reference' => NULL,
|
||||
'uid' => '1',
|
||||
'label' => '',
|
||||
))
|
||||
->execute();
|
||||
|
||||
db_create_table('tmgmt_job_item', array(
|
||||
'fields' => array(
|
||||
'tjiid' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'unsigned' => TRUE,
|
||||
),
|
||||
'plugin' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'item_type' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
),
|
||||
'item_id' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'state' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'data' => array(
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'translation' => array(
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'changed' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array(
|
||||
'tjiid',
|
||||
),
|
||||
'indexes' => array(
|
||||
'job_id' => array(
|
||||
'tjid',
|
||||
),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'job' => array(
|
||||
'table' => 'tmgmt_job',
|
||||
'columns' => array(
|
||||
'tjid',
|
||||
),
|
||||
),
|
||||
),
|
||||
'module' => 'tmgmt',
|
||||
'name' => 'tmgmt_job_item',
|
||||
));
|
||||
db_insert('tmgmt_job_item')->fields(array(
|
||||
'tjiid',
|
||||
'tjid',
|
||||
'plugin',
|
||||
'item_type',
|
||||
'item_id',
|
||||
'state',
|
||||
'data',
|
||||
'translation',
|
||||
'changed',
|
||||
))
|
||||
->values(array(
|
||||
'tjiid' => '1',
|
||||
'tjid' => '1',
|
||||
'plugin' => 'node',
|
||||
'item_type' => 'node',
|
||||
'item_id' => '1',
|
||||
'state' => '2',
|
||||
'data' => 'a:3:{s:6:"#label";s:7:"Article";s:10:"node_title";a:2:{s:6:"#label";s:5:"Title";s:5:"#text";s:12:"Test content";}s:4:"body";a:2:{s:6:"#label";s:4:"Body";i:0;a:2:{s:6:"#label";s:8:"Delta #0";s:5:"value";a:3:{s:6:"#label";s:4:"Body";s:5:"#text";s:17:"This is the body.";s:10:"#translate";b:1;}}}}',
|
||||
'translation' => 'a:2:{s:10:"node_title";a:2:{s:6:"#label";s:5:"Title";s:5:"#text";s:15:"de_Test content";}s:4:"body";a:1:{i:0;a:1:{s:5:"value";a:3:{s:6:"#label";s:4:"Body";s:5:"#text";s:20:"de_This is the body.";s:10:"#translate";b:1;}}}}',
|
||||
'changed' => '1342074125',
|
||||
))
|
||||
->values(array(
|
||||
'tjiid' => '2',
|
||||
'tjid' => '2',
|
||||
'plugin' => 'node',
|
||||
'item_type' => 'node',
|
||||
'item_id' => '1',
|
||||
'state' => '3',
|
||||
'data' => 'a:3:{s:6:"#label";s:7:"Article";s:10:"node_title";a:2:{s:6:"#label";s:5:"Title";s:5:"#text";s:12:"Test content";}s:4:"body";a:2:{s:6:"#label";s:4:"Body";i:0;a:2:{s:6:"#label";s:8:"Delta #0";s:5:"value";a:3:{s:6:"#label";s:4:"Body";s:5:"#text";s:17:"This is the body.";s:10:"#translate";b:1;}}}}',
|
||||
'translation' => 'a:2:{s:10:"node_title";a:2:{s:6:"#label";s:5:"Title";s:5:"#text";s:15:"es_Test content";}s:4:"body";a:1:{i:0;a:1:{s:5:"value";a:3:{s:6:"#label";s:4:"Body";s:5:"#text";s:20:"es_This is the body.";s:10:"#translate";b:1;}}}}',
|
||||
'changed' => '1342074127',
|
||||
))
|
||||
->execute();
|
||||
|
||||
db_create_table('tmgmt_message', array(
|
||||
'fields' => array(
|
||||
'mid' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjiid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
),
|
||||
'message' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
),
|
||||
'variables' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'created' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'type' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array(
|
||||
'mid',
|
||||
),
|
||||
'indexes' => array(
|
||||
'tjid' => array(
|
||||
'tjid',
|
||||
),
|
||||
'tjiid' => array(
|
||||
'tjiid',
|
||||
),
|
||||
),
|
||||
'module' => 'tmgmt',
|
||||
'name' => 'tmgmt_message',
|
||||
));
|
||||
db_insert('tmgmt_message')->fields(array(
|
||||
'mid',
|
||||
'tjid',
|
||||
'tjiid',
|
||||
'message',
|
||||
'variables',
|
||||
'created',
|
||||
'type',
|
||||
))
|
||||
->values(array(
|
||||
'mid' => '1',
|
||||
'tjid' => '1',
|
||||
'tjiid' => NULL,
|
||||
'message' => 'Test translator called.',
|
||||
'variables' => 'a:0:{}',
|
||||
'created' => '1342074125',
|
||||
'type' => 'debug',
|
||||
))
|
||||
->values(array(
|
||||
'mid' => '2',
|
||||
'tjid' => '1',
|
||||
'tjiid' => NULL,
|
||||
'message' => 'Test translation created.',
|
||||
'variables' => 'a:0:{}',
|
||||
'created' => '1342074125',
|
||||
'type' => 'status',
|
||||
))
|
||||
->values(array(
|
||||
'mid' => '3',
|
||||
'tjid' => '1',
|
||||
'tjiid' => '1',
|
||||
'message' => 'The translation for !source is finished and can now be reviewed.',
|
||||
'variables' => 'a:1:{s:7:"!source";s:34:"<a href="/node/1">Test content</a>";}',
|
||||
'created' => '1342074125',
|
||||
'type' => 'status',
|
||||
))
|
||||
->values(array(
|
||||
'mid' => '4',
|
||||
'tjid' => '2',
|
||||
'tjiid' => NULL,
|
||||
'message' => 'Test translator called.',
|
||||
'variables' => 'a:0:{}',
|
||||
'created' => '1342074127',
|
||||
'type' => 'debug',
|
||||
))
|
||||
->values(array(
|
||||
'mid' => '5',
|
||||
'tjid' => '2',
|
||||
'tjiid' => NULL,
|
||||
'message' => 'Test translation created.',
|
||||
'variables' => 'a:0:{}',
|
||||
'created' => '1342074127',
|
||||
'type' => 'status',
|
||||
))
|
||||
->values(array(
|
||||
'mid' => '6',
|
||||
'tjid' => '2',
|
||||
'tjiid' => '2',
|
||||
'message' => 'The translation for !source is finished and can now be reviewed.',
|
||||
'variables' => 'a:1:{s:7:"!source";s:34:"<a href="/node/1">Test content</a>";}',
|
||||
'created' => '1342074127',
|
||||
'type' => 'status',
|
||||
))
|
||||
->execute();
|
||||
|
||||
db_create_table('tmgmt_translator', array(
|
||||
'fields' => array(
|
||||
'tid' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'label' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'description' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
),
|
||||
'plugin' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'settings' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'weight' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'status' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
'module' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'primary key' => array(
|
||||
'tid',
|
||||
),
|
||||
'unique keys' => array(
|
||||
'name' => array(
|
||||
'name',
|
||||
),
|
||||
),
|
||||
'module' => 'tmgmt',
|
||||
'name' => 'tmgmt_translator',
|
||||
));
|
||||
db_insert('tmgmt_translator')->fields(array(
|
||||
'tid',
|
||||
'name',
|
||||
'label',
|
||||
'description',
|
||||
'plugin',
|
||||
'settings',
|
||||
'weight',
|
||||
'status',
|
||||
'module',
|
||||
))
|
||||
->values(array(
|
||||
'tid' => '1',
|
||||
'name' => 'test_translator',
|
||||
'label' => 'Test translator (auto created)',
|
||||
'description' => 'Simple translator for testing purposes.',
|
||||
'plugin' => 'test_translator',
|
||||
'settings' => 'a:2:{s:11:"auto_accept";b:0;s:15:"expose_settings";b:1;}',
|
||||
'weight' => '0',
|
||||
'status' => '1',
|
||||
'module' => NULL,
|
||||
))
|
||||
->execute();
|
@@ -0,0 +1,16 @@
|
||||
name = Translation Management Test plugins
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
dependencies[] = tmgmt
|
||||
files[] = tmgmt_test.plugin.source.inc
|
||||
files[] = tmgmt_test.plugin.html_source.inc
|
||||
files[] = tmgmt_test.plugin.translator.inc
|
||||
files[] = tmgmt_test.ui.translator.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Module file of the translation management test module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_translator_plugin_info().
|
||||
*/
|
||||
function tmgmt_test_tmgmt_translator_plugin_info() {
|
||||
return array(
|
||||
'test_translator' => array(
|
||||
'label' => t('Test translator'),
|
||||
'description' => t('Simple translator for testing purposes.'),
|
||||
'plugin controller class' => 'TMGMTTestTranslatorPluginController',
|
||||
'ui controller class' => 'TMGMTTestTranslatorUIController',
|
||||
'default settings' => array(
|
||||
'expose_settings' => TRUE,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_plugin_info().
|
||||
*/
|
||||
function tmgmt_test_tmgmt_source_plugin_info() {
|
||||
return array(
|
||||
'test_source' => array(
|
||||
'label' => t('Test source'),
|
||||
'description' => t('Simple source for testing purposes.'),
|
||||
'plugin controller class' => 'TMGMTTestSourcePluginController',
|
||||
),
|
||||
'test_html_source' => array(
|
||||
'label' => t('Test html source'),
|
||||
'description' => t('HTML source for testing purposes.'),
|
||||
'plugin controller class' => 'TMGMTTestHTMLSourcePluginController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_suggestions().
|
||||
*/
|
||||
function tmgmt_test_tmgmt_source_suggestions(array $items, TMGMTJob $job) {
|
||||
$suggestions = array();
|
||||
foreach ($items as $item) {
|
||||
if ($item->plugin == 'test_source') {
|
||||
$suggestions[] = array(
|
||||
'job_item' => tmgmt_job_item_create('test_source', $item->item_type . '_suggestion', $item->item_id),
|
||||
'reason' => t('Test suggestion for @type source @id', array('@type' => $item->item_type,'@id' => $item->item_id)),
|
||||
'from_item' => $item->tjiid,
|
||||
);
|
||||
}
|
||||
}
|
||||
return $suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_fle_text_processor_plugin_info().
|
||||
*/
|
||||
function tmgmt_test_tmgmt_file_text_processor_plugin_info() {
|
||||
return array(
|
||||
'test' => array(
|
||||
'label' => t('Test'),
|
||||
'plugin controller class' => 'TMGMTTestTextProcessor',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function tmgmt_test_menu() {
|
||||
$items['tmgmt-add-to-cart/%tmgmt_job_item'] = array(
|
||||
'title' => 'Add to cart',
|
||||
'description' => 'Provides the possibility to add testing job items into the cart.',
|
||||
'page callback' => 'tmgmt_test_add_to_cart',
|
||||
'page arguments' => array(1),
|
||||
'access callback' => TRUE,
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to add given job item into the cart.
|
||||
*/
|
||||
function tmgmt_test_add_to_cart(TMGMTJobITem $job_item) {
|
||||
tmgmt_ui_cart_get()->addExistingJobItems(array($job_item));
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the test source plugin with html.
|
||||
*/
|
||||
|
||||
class TMGMTTestHTMLSourcePluginController extends TMGMTTestSourcePluginController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
return array(
|
||||
'dummy' => array(
|
||||
'deep_nesting' => array(
|
||||
'#text' => file_get_contents(drupal_get_path('module', 'tmgmt') . '/tests/testing_html/sample.html'),
|
||||
'#label' => 'Label for job item with type ' . $job_item->item_type . ' and id ' . $job_item->item_id . '.',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the test source plugin.
|
||||
*/
|
||||
|
||||
class TMGMTTestSourcePluginController extends TMGMTDefaultSourcePluginController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUri(TMGMTJobItem $job_item) {
|
||||
// Provide logic which allows to test for source which is either accessible
|
||||
// or not accessible to anonymous user. This is may then be used to test if
|
||||
// the source url is attached to the job comment sent to a translation
|
||||
// service.
|
||||
$path = 'node';
|
||||
if ($job_item->item_type == 'test_not_accessible') {
|
||||
$path = 'admin';
|
||||
}
|
||||
return array('path' => $path, 'options' => array());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
$label = $this->pluginType . ':' . $job_item->item_type . ':' . $job_item->item_id;
|
||||
|
||||
// We need to test if job and job item labels get properly truncated,
|
||||
// therefore in case the job item type is "test_with_long_label" we append
|
||||
// further text to the existing label.
|
||||
if ($job_item->item_type == 'test_with_long_label') {
|
||||
$label .= 'Some very long and boring label that definitely exceeds hundred and twenty eight characters which is the maximum character count for the job item label.';
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
// Allow tests to set custom source data.
|
||||
$source = variable_get('tmgmt_test_source_data', array(
|
||||
'dummy' => array(
|
||||
'deep_nesting' => array(
|
||||
'#text' => 'Text for job item with type @type and id @id.',
|
||||
'#label' => 'Label for job item with type @type and id @id.',
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$variables = array(
|
||||
'@type' => $job_item->item_type,
|
||||
'@id' => $job_item->item_id,
|
||||
);
|
||||
|
||||
$this->replacePlaceholders($source, $variables);
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will replace placeholders in the #text offsets.
|
||||
*
|
||||
* @param array $data
|
||||
* Data structures where to replace placeholders.
|
||||
* @param $variables
|
||||
* Key value pairs.
|
||||
*/
|
||||
protected function replacePlaceholders(&$data, $variables) {
|
||||
foreach (element_children($data) as $key) {
|
||||
if (isset($data[$key]['#text'])) {
|
||||
$data[$key]['#text'] = format_string($data[$key]['#text'], $variables);
|
||||
}
|
||||
else {
|
||||
$this->replacePlaceholders($data[$key], $variables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item) {
|
||||
// Set a variable that can be checked later for a given job item.
|
||||
variable_set('tmgmt_test_saved_translation_' . $job_item->item_type . '_' . $job_item->item_id, TRUE);
|
||||
$job_item->accepted();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
return array_keys(language_list());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item) {
|
||||
$source_languages = variable_get('tmgmt_test_source_languages', array());
|
||||
if (isset($source_languages[$job_item->tjiid])) {
|
||||
return $source_languages[$job_item->tjiid];
|
||||
}
|
||||
|
||||
return 'en';
|
||||
}
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Cotains the test translator plugin.
|
||||
*/
|
||||
|
||||
class TMGMTTestTranslatorPluginController extends TMGMTDefaultTranslatorPluginController implements TMGMTTranslatorRejectDataItem {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $escapeStart = '[[[';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $escapeEnd = ']]]';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultRemoteLanguagesMappings() {
|
||||
return array(
|
||||
'en' => 'en-us',
|
||||
'de' => 'de-ch',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCheckoutSettings(TMGMTJob $job) {
|
||||
return $job->getTranslator()->getSetting('expose_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function requestTranslation(TMGMTJob $job) {
|
||||
// Add a debug message.
|
||||
$job->addMessage('Test translator called.', array(), 'debug');
|
||||
|
||||
// Do something different based on the action, if defined.
|
||||
$action = isset($job->settings['action']) ? $job->settings['action'] : '';
|
||||
switch ($action) {
|
||||
case 'submit':
|
||||
$job->submitted('Test submit.');
|
||||
break;
|
||||
|
||||
case 'reject':
|
||||
$job->rejected('This is not supported.');
|
||||
break;
|
||||
|
||||
case 'fail':
|
||||
// Target not reachable.
|
||||
$job->addMessage('Service not reachable.', array(), 'error');
|
||||
break;
|
||||
|
||||
case 'translate':
|
||||
default:
|
||||
// The dummy translation prefixes strings with the target language.
|
||||
$data = array_filter(tmgmt_flatten_data($job->getData()), '_tmgmt_filter_data');
|
||||
$tdata = array();
|
||||
foreach ($data as $key => $value) {
|
||||
$tdata[$key]['#text'] = $job->target_language . '_' . $value['#text'];
|
||||
}
|
||||
$job->submitted('Test translation created.');
|
||||
$job->addTranslatedData(tmgmt_unflatten_data($tdata));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function canTranslate(TMGMTTranslator $translator, TMGMTJob $job) {
|
||||
if (isset($job->settings['action']) && $job->settings['action'] == 'not_translatable') {
|
||||
return FALSE;
|
||||
}
|
||||
return parent::canTranslate($translator, $job);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedTargetLanguages(TMGMTTranslator $translator, $source_language) {
|
||||
$languages = drupal_map_assoc(array('en', 'de', 'es', 'it', 'zh-hans', 'gsw-berne'));
|
||||
unset($languages[$source_language]);
|
||||
return $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rejectDataItem(TMGMTJobItem $job_item, array $key, array $values = NULL) {
|
||||
$key = '[' . implode('][', $key) . ']';
|
||||
$job_item->addMessage('Rejected data item @key for job item @item in job @job.', array('@key' => $key, '@item' => $job_item->tjiid, '@job' => $job_item->tjid));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rejectForm($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the test translator UI plugin.
|
||||
*/
|
||||
|
||||
class TMGMTTestTranslatorUIController extends TMGMTDefaultTranslatorUIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function pluginSettingsForm($form, &$form_state, TMGMTTranslator $translator, $busy = FALSE) {
|
||||
$form['expose_settings'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Display settings'),
|
||||
'#default_value' => TRUE,
|
||||
);
|
||||
|
||||
$form['action'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Default action'),
|
||||
'#options' => array(
|
||||
'translate' => t('Translate'),
|
||||
'submit' => t('Submit'),
|
||||
'reject' => t('Reject'),
|
||||
'fail' => t('Fail'),
|
||||
'not_translatable' => t('Not translatable'),
|
||||
),
|
||||
);
|
||||
return parent::pluginSettingsForm($form, $form_state, $translator, $busy);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkoutSettingsForm($form, &$form_state, TMGMTJob $job) {
|
||||
if ($job->getTranslator()->getSetting('expose_settings')) {
|
||||
$form['action'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Action'),
|
||||
'#options' => array(
|
||||
'translate' => t('Translate'),
|
||||
'submit' => t('Submit'),
|
||||
'reject' => t('Reject'),
|
||||
'fail' => t('Fail'),
|
||||
'not_translatable' => t('Not translatable'),
|
||||
),
|
||||
'#default_value' => $job->getTranslator()->getSetting('action'),
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reviewDataItemElement($form, &$form_state, $data_item_key, $parent_key, array $data_item, TMGMTJobItem $item) {
|
||||
$form['below'] = array(
|
||||
'#markup' => t('Testing output of review data item element @key from the testing translator.', array('@key' => $data_item_key))
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
}
|
272
sites/all/modules/contrib/localisation/tmgmt/tmgmt.api.php
Normal file
272
sites/all/modules/contrib/localisation/tmgmt/tmgmt.api.php
Normal file
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Translation Management module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup tmgmt_source
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide information about source plugins.
|
||||
*
|
||||
* @see TMGMTTestSourcePluginController
|
||||
*/
|
||||
function hook_tmgmt_source_plugin_info() {
|
||||
return array(
|
||||
'test_source' => array(
|
||||
'label' => t('Test source'),
|
||||
'description' => t('Simple source for testing purposes.'),
|
||||
'controller class' => 'TMGMTTestSourcePluginController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter source plugins information.
|
||||
*
|
||||
* @param $info
|
||||
* The defined source plugin information.
|
||||
*
|
||||
* @see hook_tmgmt_source_plugin_info()
|
||||
*/
|
||||
function hook_tmgmt_source_plugin_info_alter(&$info) {
|
||||
$info['test_source']['description'] = t('Updated description');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of suggested sources for job items.
|
||||
*
|
||||
* @param array $items
|
||||
* An array with TMGMTJobItem objects which must be checked for suggested
|
||||
* translations.
|
||||
* - TMGMTJobItem A JobItem to check for suggestions.
|
||||
* - ...
|
||||
* @param TMGMTJob $job
|
||||
* The current translation job to check for additional translation items.
|
||||
*
|
||||
* @return array
|
||||
* An array with all additional translation suggestions.
|
||||
* - job_item: A TMGMTJobItem instance.
|
||||
* - referenced: A string which indicates where this suggestion comes from.
|
||||
* - from_job: The main TMGMTJob-ID which suggests this translation.
|
||||
*/
|
||||
function hook_tmgmt_source_suggestions(array $items, TMGMTJob $job) {
|
||||
return array(
|
||||
array(
|
||||
'job_item' => tmgmt_job_item_create('entity', 'node', 0),
|
||||
'reason' => t('Referenced @type of field @label', array('@type' => 'entity', '@label' => 'label')),
|
||||
'from_item' => $items[1]->tjiid,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup tmgmt_source".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup tmgmt_translator
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide information about translator plugins.
|
||||
*
|
||||
* @see TMGMTTestTranslatorPluginController
|
||||
*/
|
||||
function hook_tmgmt_translator_plugin_info() {
|
||||
return array(
|
||||
'test_translator' => array(
|
||||
'label' => t('Test translator'),
|
||||
'description' => t('Simple translator for testing purposes.'),
|
||||
'plugin controller class' => 'TMGMTTestTranslatorPluginController',
|
||||
'ui controller class' => 'TMGMTTestTranslatorUIController',
|
||||
'default settings' => array(
|
||||
'expose_settings' => TRUE,
|
||||
),
|
||||
// By default, a translator is automatically created with the default
|
||||
// settings. Set auto create to FALSE to prevent this.
|
||||
'auto create' => TRUE,
|
||||
// If the translator should provide remote languages mappings feature.
|
||||
// It defaults to TRUE.
|
||||
'map remote languages' => FALSE,
|
||||
// Flag defining if job settings are handled by plugin itself.
|
||||
// Defaults to FALSE.
|
||||
'job settings custom handling' => FALSE,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter information about translator plugins.
|
||||
*/
|
||||
function hook_tmgmt_translator_plugin_info_alter(&$info) {
|
||||
$info['test_source']['description'] = t('Updated description');
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup tmgmt_translator".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tmgmt_job Translation Jobs
|
||||
*
|
||||
* A single task to translate something into a given language using a @link
|
||||
* translator translator @endlink.
|
||||
*
|
||||
* Attached to these jobs are job items, which specify which @link source
|
||||
* sources @endlink are to be translated.
|
||||
*
|
||||
* To create a new translation job, first create a job and then assign items to
|
||||
* each. Each item needs to specify the source plugin that should be used
|
||||
* and the type and id, which the source plugin then uses to identify it later
|
||||
* on.
|
||||
*
|
||||
* @code
|
||||
* $job = tmgmt_job_create('en', $target_language);
|
||||
*
|
||||
* for ($i = 1; $i < 3; $i++) {
|
||||
* $job->addItem('test_source', 'test', $i);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Once a job has been created, it can be assigned to a translator plugin, which
|
||||
* is the service that is going to do the translation.
|
||||
*
|
||||
* @code
|
||||
* $job->translator = 'test_translator';
|
||||
* // Translator specific settings.
|
||||
* $job->settings = array(
|
||||
* 'priority' => 5,
|
||||
* );
|
||||
* $job->save();
|
||||
*
|
||||
* // Get the translator plugin and request a translation.
|
||||
* if ($job->isTranslatable()) {
|
||||
* $job->requestTranslation();
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* The translation plugin will then request the text from the source plugin.
|
||||
* Depending on the plugin, the text might be sent to an external service
|
||||
* or assign it to a local user or team of users. At some point, a translation
|
||||
* will be returned and saved in the job items.
|
||||
*
|
||||
* The translation can now be reviewed, accepted and the source plugins be told
|
||||
* to save the translation.
|
||||
*
|
||||
* @code
|
||||
* $job->accepted('Optional message');
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tmgmt_translator Translators
|
||||
*
|
||||
* A translator plugin integrates a translation service.
|
||||
*
|
||||
* To define a translator, hook_tmgmt_translator_plugin_info() needs to be
|
||||
* implemented and a controller class (specified in the info) created.
|
||||
*
|
||||
* A translator plugin is then responsible for sending out a translation job and
|
||||
* storing the translated texts back into the job and marking it as needs review
|
||||
* once it's finished.
|
||||
*
|
||||
* TBD.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tmgmt_source Translation source
|
||||
*
|
||||
* A source plugin represents translatable elements on a site.
|
||||
*
|
||||
* For example nodes, but also plain strings, menu items, other entities and so
|
||||
* on.
|
||||
*
|
||||
* To define a source, hook_tmgmt_source_plugin_info() needs to be implemented
|
||||
* and a controller class (specified in the info) created.
|
||||
*
|
||||
* A source has three separate tasks.
|
||||
*
|
||||
* - Allows to create a new @link job translation job @endlink and assign job
|
||||
* items to itself.
|
||||
* - Extract the translatable text into a nested array when
|
||||
* requested to do in their implementation of
|
||||
* TMGMTSourcePluginControllerInterface::getData().
|
||||
* - Save the accepted translations returned by the translation plugin in their
|
||||
* sources in their implementation of
|
||||
* TMGMTSourcePluginControllerInterface::saveTranslation().
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tmgmt_remote_languages_mapping Remote languages mapping
|
||||
*
|
||||
* Logic to deal with different language codes at client and server that stand
|
||||
* for the same language.
|
||||
*
|
||||
* Each tmgmt plugin is expected to support this feature. However for those
|
||||
* plugins where such feature has no use there is a plugin setting
|
||||
* "map remote languages" which can be set to FALSE.
|
||||
*
|
||||
* @section mappings_info Mappings info
|
||||
*
|
||||
* There are several methods defined by
|
||||
* TMGMTTranslatorPluginControllerInterface and implemented in
|
||||
* TMGMTDefaultTranslatorPluginController that deal with mappings info.
|
||||
*
|
||||
* - getRemoteLanguagesMappings() - provides pairs of local_code => remote_code.
|
||||
* - mapToRemoteLanguage() & mapToLocalLanguage() - helpers to map local/remote.
|
||||
* Note that methods with same names and functionality are provided by the
|
||||
* TMGMTTranslator entity. These are convenience methods.
|
||||
*
|
||||
* The above methods should not need reimplementation unless special logic is
|
||||
* needed. However following methods provide only the fallback behaviour and
|
||||
* therefore it is recommended that each plugin provides its specific
|
||||
* implementation.
|
||||
*
|
||||
* - getDefaultRemoteLanguagesMappings() - we might know some mapping pairs
|
||||
* prior to configuring a plugin, this is the place where we can define these
|
||||
* mappings. The default implementation returns an empty array.
|
||||
* - getSupportedRemoteLanguages() - gets array of language codes in lang_code =>
|
||||
* lang_code format. It says with what languages the remote system can deal
|
||||
* with. These codes are in the remote format.
|
||||
*
|
||||
* @section mapping_remote_to_local Mapping remote to local
|
||||
*
|
||||
* Mapping remote to local language codes is done when determining the
|
||||
* language capabilities of the remote system. All following logic should then
|
||||
* solely work with local language codes. There are two methods defined by
|
||||
* the TMGMTTranslatorPluginControllerInterface interface. To do the mapping
|
||||
* a plugin must implement getSupportedTargetLanguages().
|
||||
*
|
||||
* - getSupportedTargetLanguages() - should return local language codes. So
|
||||
* within this method the mapping needs to be executed.
|
||||
* - getSupportedLanguagePairs() - gets language pairs for which translations
|
||||
* can be done. The language codes must be in local form. The default
|
||||
* implementation uses getSupportedTargetLanguages() so mapping occur. However
|
||||
* this approach is not effective and therefore each plugin should provide
|
||||
* its specific implementation with regard to performance.
|
||||
*
|
||||
* @section mapping_local_to_remote Mapping local to remote
|
||||
*
|
||||
* Mapping of local to remote language codes is done upon translation job
|
||||
* request in the TMGMTTranslatorPluginControllerInterface::requestTranslation()
|
||||
* plugin implementation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tmgmt_ui_cart Translation cart
|
||||
*
|
||||
* The translation cart can collect multiple source items of different types
|
||||
* which are meant for translation into a list. The list then provides
|
||||
* functionality to request translation of the items into multiple target
|
||||
* languages.
|
||||
*
|
||||
* Each source can easily plug into the cart system utilising the
|
||||
* tmgmt_ui_add_cart_form() on either the source overview page as well as the
|
||||
* translate tab.
|
||||
*/
|
58
sites/all/modules/contrib/localisation/tmgmt/tmgmt.info
Normal file
58
sites/all/modules/contrib/localisation/tmgmt/tmgmt.info
Normal file
@@ -0,0 +1,58 @@
|
||||
name = Translation Management Core
|
||||
description = Core functionality for the Translation Management Suite.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = entity
|
||||
dependencies[] = locale
|
||||
dependencies[] = views
|
||||
simplytest_dependencies[] = tmgmt_demo
|
||||
|
||||
files[] = includes/tmgmt.exception.inc
|
||||
files[] = controller/tmgmt.controller.job.inc
|
||||
files[] = controller/tmgmt.controller.job_item.inc
|
||||
files[] = controller/tmgmt.controller.remote.inc
|
||||
files[] = controller/tmgmt.controller.translator.inc
|
||||
files[] = entity/tmgmt.entity.job.inc
|
||||
files[] = entity/tmgmt.entity.job_item.inc
|
||||
files[] = entity/tmgmt.entity.message.inc
|
||||
files[] = entity/tmgmt.entity.remote.inc
|
||||
files[] = entity/tmgmt.entity.translator.inc
|
||||
files[] = plugin/tmgmt.plugin.base.inc
|
||||
files[] = plugin/tmgmt.plugin.interface.base.inc
|
||||
files[] = plugin/tmgmt.plugin.interface.reject.inc
|
||||
files[] = plugin/tmgmt.plugin.interface.source.inc
|
||||
files[] = plugin/tmgmt.plugin.interface.translator.inc
|
||||
files[] = plugin/tmgmt.plugin.source.inc
|
||||
files[] = plugin/tmgmt.plugin.translator.inc
|
||||
files[] = plugin/tmgmt.ui.interface.source.inc
|
||||
files[] = plugin/tmgmt.ui.interface.translator.inc
|
||||
files[] = plugin/tmgmt.ui.source.inc
|
||||
files[] = plugin/tmgmt.ui.translator.inc
|
||||
files[] = includes/tmgmt.info.inc
|
||||
files[] = tests/tmgmt.base.test
|
||||
files[] = tests/tmgmt.base.entity.test
|
||||
files[] = tests/tmgmt.crud.test
|
||||
files[] = tests/tmgmt.plugin.test
|
||||
files[] = tests/tmgmt.helper.test
|
||||
files[] = tests/tmgmt.upgrade.alpha1.test
|
||||
|
||||
; Views integration and handlers
|
||||
files[] = views/tmgmt.views.inc
|
||||
files[] = views/handlers/tmgmt_handler_field_tmgmt_entity_label.inc
|
||||
files[] = views/handlers/tmgmt_handler_field_tmgmt_job_item_type.inc
|
||||
files[] = views/handlers/tmgmt_handler_field_tmgmt_translator.inc
|
||||
files[] = views/handlers/tmgmt_handler_field_tmgmt_job_operations.inc
|
||||
files[] = views/handlers/tmgmt_handler_field_tmgmt_progress.inc
|
||||
files[] = views/handlers/tmgmt_handler_field_tmgmt_wordcount.inc
|
||||
files[] = views/handlers/tmgmt_handler_field_tmgmt_message_message.inc
|
||||
files[] = views/handlers/tmgmt_handler_field_tmgmt_job_item_operations.inc
|
||||
files[] = views/handlers/tmgmt_handler_field_tmgmt_job_item_count.inc
|
||||
files[] = views/plugins/tmgmt_views_job_access.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
877
sites/all/modules/contrib/localisation/tmgmt/tmgmt.install
Normal file
877
sites/all/modules/contrib/localisation/tmgmt/tmgmt.install
Normal file
@@ -0,0 +1,877 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Installation hooks for the Translation Management module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function tmgmt_uninstall() {
|
||||
// Remove all variables that we added.
|
||||
variable_del('tmgmt_quick_checkout');
|
||||
variable_del('tmgmt_purge_finished');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function tmgmt_schema() {
|
||||
$schema['tmgmt_job'] = array(
|
||||
'description' => 'A translation job represents a translation order that can be assigned to a translator.',
|
||||
'fields' => array(
|
||||
'tjid' => array(
|
||||
'description' => 'The identifier of the translation job.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'source_language' => array(
|
||||
'description' => 'The source language of the data.',
|
||||
'type' => 'varchar',
|
||||
'length' => 12,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'target_language' => array(
|
||||
'description' => 'The language the data should be translated to.',
|
||||
'type' => 'varchar',
|
||||
'length' => 12,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'state' => array(
|
||||
'description' => 'The state of the translation job.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'created' => array(
|
||||
'description' => 'Created timestamp.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'Changed timestamp.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'translator' => array(
|
||||
'description' => 'Machine name of the translator.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
),
|
||||
'settings' => array(
|
||||
'description' => 'Translator specific configuration and context for this job.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'reference' => array(
|
||||
'description' => 'Remote identifier of this translation job.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'uid of the job creator',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'label' => array(
|
||||
'description' => 'Optional user provided label of the job.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
),
|
||||
),
|
||||
'primary key' => array('tjid'),
|
||||
'indexes' => array(
|
||||
'state' => array('state'),
|
||||
'reference' => array('reference'),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['tmgmt_remote'] = array(
|
||||
'description' => 'TMGMT job remote mapping.',
|
||||
'fields' => array(
|
||||
'trid' => array(
|
||||
'description' => 'The primary key.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => '{tmgmt_job}.tjid foreign key',
|
||||
),
|
||||
'tjiid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => '{tmgmt_job_item}.tjjid foreign key',
|
||||
),
|
||||
'data_item_key' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Translation job data item key.',
|
||||
),
|
||||
'remote_identifier_1' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 127,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Custom remote identifier data.',
|
||||
),
|
||||
'remote_identifier_2' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 127,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Custom remote identifier data.',
|
||||
),
|
||||
'remote_identifier_3' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Custom remote identifier data.',
|
||||
),
|
||||
'remote_url' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Remote job url.',
|
||||
),
|
||||
'word_count' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Word count info provided by the remote service.',
|
||||
),
|
||||
'amount' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'big',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Amount charged for the remote translation job.',
|
||||
),
|
||||
'currency' => array(
|
||||
'type' => 'char',
|
||||
'length' => 3,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Amount charged currency.',
|
||||
),
|
||||
'remote_data' => array(
|
||||
'description' => 'Custom remote data.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('trid'),
|
||||
'indexes' => array(
|
||||
'tjid' => array('tjid'),
|
||||
'tjiid' => array('tjiid'),
|
||||
'remote_identifiers' => array('remote_identifier_1', 'remote_identifier_2'),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['tmgmt_job_item'] = array(
|
||||
'description' => 'A job item connects a source to a translation job.',
|
||||
'fields' => array(
|
||||
'tjiid' => array(
|
||||
'description' => 'The identifier of the translation job item.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjid' => array(
|
||||
'description' => 'The identifier of the translation job.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'unsigned' => TRUE,
|
||||
),
|
||||
'plugin' => array(
|
||||
'description' => 'Indicates the plugin which provides this item.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'item_type' => array(
|
||||
'description' => 'The type of the item, e.g. the entity type.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
),
|
||||
'item_id' => array(
|
||||
'description' => 'The unique id (within the given item type) of the item.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'state' => array(
|
||||
'description' => 'The state of the translation job item.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'data' => array(
|
||||
'description' => 'Can be used by the source plugin to store the data if it can not be retrieved anymore later on.',
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'Changed timestamp.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'count_pending' => array(
|
||||
'description' => 'Counter for all pending data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'count_translated' => array(
|
||||
'description' => 'Counter for all translated data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'count_accepted' => array(
|
||||
'description' => 'Counter for all accepted data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'count_reviewed' => array(
|
||||
'description' => 'Counter for all reviewed data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'word_count' => array(
|
||||
'description' => 'Word count of all texts contained in this job item.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('tjiid'),
|
||||
'indexes' => array(
|
||||
'job_id' => array('tjid'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'job' => array(
|
||||
'table' => 'tmgmt_job',
|
||||
'columns' => array('tjid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['tmgmt_translator'] = array(
|
||||
'description' => 'A translator is a combination of a translator type and type specific configuration.',
|
||||
'fields' => array(
|
||||
'tid' => array(
|
||||
'description' => 'The identifier of the translator.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => 'Machine name identifier of the translator.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'label' => array(
|
||||
'description' => 'Label of the translator.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => 'Description of the translator.',
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
),
|
||||
'plugin' => array(
|
||||
'description' => 'Name of the translator service plugin.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'settings' => array(
|
||||
'description' => 'Translator specific settings.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'weight' => array(
|
||||
'description' => 'The weight of the translator.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'The exportable status of the entity.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
// Set the default to ENTITY_CUSTOM without using the constant as it is
|
||||
// not safe to use it at this point.
|
||||
'default' => 0x01,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
'module' => array(
|
||||
'description' => 'The name of the providing module if the entity has been defined in code.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('tid'),
|
||||
'unique keys' => array(
|
||||
'name' => array('name'),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['tmgmt_message'] = array(
|
||||
'description' => 'A log message can be used to store events that affect a job.',
|
||||
'fields' => array(
|
||||
'mid' => array(
|
||||
'description' => 'The identifier of the message.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjid' => array(
|
||||
'description' => 'The identifier of the translation job that the message belongs to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjiid' => array(
|
||||
'description' => 'The identifier of the translation job item that the message belongs to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The identifier of the user who performed the action.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'message' => array(
|
||||
'description' => 'The language into the data should be translated.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
),
|
||||
'variables' => array(
|
||||
'description' => 'The variables of the message as expected by t().',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'created' => array(
|
||||
'description' => 'Created timestamp.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'type' => array(
|
||||
'description' => 'Type of the message (debug, notice, warning or error)',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('mid'),
|
||||
'indexes' => array(
|
||||
'tjid' => array('tjid'),
|
||||
'tjiid' => array('tjiid'),
|
||||
),
|
||||
);
|
||||
|
||||
// Clone the schema for our cache table from Drupal core.
|
||||
$schema['cache_tmgmt'] = drupal_get_schema_unprocessed('system', 'cache');
|
||||
|
||||
// Clone the schema for the entity cache module if it is enabled.
|
||||
if (module_exists('entitycache')) {
|
||||
$schema['cache_entity_tmgmt_translator'] = drupal_get_schema_unprocessed('system', 'cache');
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the content of the 'translation' field into the 'data' field.
|
||||
*/
|
||||
function tmgmt_update_7000(&$sandbox) {
|
||||
if (!isset($sandbox['progress'])) {
|
||||
$sandbox['progress'] = 0;
|
||||
$sandbox['max'] = db_query('SELECT COUNT(*) FROM {tmgmt_job_item}')->fetchField();
|
||||
}
|
||||
|
||||
$results = db_select('tmgmt_job_item', 'tji')
|
||||
->fields('tji', array('tjiid', 'data', 'translation'))
|
||||
->range($sandbox['progress'], 50)
|
||||
->orderBy('tjiid', 'ASC')
|
||||
->execute();
|
||||
|
||||
foreach ($results as $item) {
|
||||
$data = unserialize($item->data);
|
||||
if (!empty($item->translation)) {
|
||||
foreach (tmgmt_flatten_data(unserialize($item->translation)) as $key => $translation) {
|
||||
if (!empty($item->data)) {
|
||||
$key = explode('][', $key);
|
||||
drupal_array_set_nested_value($data, array_merge($key, array('#translation')), $translation);
|
||||
}
|
||||
}
|
||||
db_update('tmgmt_job_item')
|
||||
->condition('tjiid', $item->tjiid)
|
||||
->fields(array('data' => serialize($data)))
|
||||
->execute();
|
||||
}
|
||||
|
||||
$sandbox['progress']++;
|
||||
}
|
||||
|
||||
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the 'translation' field from the job item entity.
|
||||
*/
|
||||
function tmgmt_update_7001() {
|
||||
db_drop_field('tmgmt_job_item', 'translation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add counter columns to {tmgmt_job_item}.
|
||||
*/
|
||||
function tmgmt_update_7002() {
|
||||
// Defining schema of additional fields.
|
||||
$schema = array(
|
||||
'count_pending' => array(
|
||||
'description' => 'Counter for all pending data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'count_translated' => array(
|
||||
'description' => 'Counter for all translated data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'count_approved' => array(
|
||||
'description' => 'Counter for all approved data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
);
|
||||
// Add aditional fields to db.
|
||||
foreach ($schema as $field => $spec) {
|
||||
db_add_field('tmgmt_job_item', $field, $spec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data parsing helper function for tmgmt_update_7003().
|
||||
*
|
||||
* Copied from TMGMTJobItemController::count().
|
||||
*
|
||||
* @param $item
|
||||
* The current data item.
|
||||
* @param $entity
|
||||
* The job item the count should be calculated.
|
||||
*/
|
||||
function _tmgmt_data_count_7003(&$item, $entity) {
|
||||
if (!empty($item['#text'])) {
|
||||
if (_tmgmt_filter_data($item)) {
|
||||
|
||||
// Set default states if no state is set.
|
||||
if (!isset($item['#status'])) {
|
||||
// Translation is present.
|
||||
if (!empty($item['#translation'])) {
|
||||
$item['#status'] = TMGMT_DATA_ITEM_STATE_TRANSLATED;
|
||||
}
|
||||
// No translation present.
|
||||
else {
|
||||
$item['#status'] = TMGMT_DATA_ITEM_STATE_PENDING;
|
||||
}
|
||||
}
|
||||
switch ($item['#status']) {
|
||||
case 1:
|
||||
$entity->count_approved++;
|
||||
break;
|
||||
case 2:
|
||||
$entity->count_translated++;
|
||||
break;
|
||||
default:
|
||||
$entity->count_pending++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach (element_children($item) as $key) {
|
||||
_tmgmt_data_count_7003($item[$key], $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set counters for existing job items.
|
||||
*/
|
||||
function tmgmt_update_7003(&$sandbox) {
|
||||
if (!isset($sandbox['progress'])) {
|
||||
$sandbox['progress'] = 0;
|
||||
$sandbox['last_tjiid'] = 0;
|
||||
$sandbox['max'] = db_query('SELECT COUNT(tjiid) FROM {tmgmt_job_item}')->fetchField();
|
||||
}
|
||||
$result = db_query('SELECT tjiid, data FROM {tmgmt_job_item} WHERE tjiid > :last_tjiid ORDER BY tjiid LIMIT 10', array( ':last_tjiid' => $sandbox['last_tjiid']));
|
||||
foreach ($result as $row) {
|
||||
// Unseralize data, count it and then save the counters.
|
||||
if (!empty($row->data)) {
|
||||
$fake_job_item = (object)array(
|
||||
'count_approved' => 0,
|
||||
'count_translated' => 0,
|
||||
'count_pending' => 0,
|
||||
);
|
||||
$data = unserialize($row->data);
|
||||
_tmgmt_data_count_7003($data, $fake_job_item);
|
||||
$fields = (array)$fake_job_item + array(
|
||||
'data' => serialize($data),
|
||||
);
|
||||
db_update('tmgmt_job_item')
|
||||
->condition('tjiid', $row->tjiid)
|
||||
->fields($fields)
|
||||
->execute();
|
||||
}
|
||||
$sandbox['progress']++;
|
||||
}
|
||||
|
||||
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
|
||||
if ($row) {
|
||||
$sandbox['last_tjiid'] = $row->tjiid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the [#translation][#finished] attribute with [#status].
|
||||
*/
|
||||
function tmgmt_update_7004(&$sandbox) {
|
||||
if (!isset($sandbox['progress'])) {
|
||||
$sandbox['progress'] = 0;
|
||||
$sandbox['last_tjiid'] = 0;
|
||||
$sandbox['max'] = db_query('SELECT COUNT(tjiid) FROM {tmgmt_job_item}')->fetchField();
|
||||
}
|
||||
$result = db_query('SELECT tjiid, data FROM {tmgmt_job_item} WHERE tjiid > :last_tjiid ORDER BY tjiid LIMIT 10', array(':last_tjiid' => $sandbox['last_tjiid']));
|
||||
foreach ($result as $row) {
|
||||
if (!empty($row->data)) {
|
||||
$data = unserialize($row->data);
|
||||
$flattened_data = array_filter(tmgmt_flatten_data($data), '_tmgmt_filter_data');
|
||||
// Loop over data, find finished translations, remove the flag and set
|
||||
// the status instead.
|
||||
foreach ($flattened_data as $key => $values) {
|
||||
if (!empty($values['#translation']['#finished'])) {
|
||||
$finished = $values['#translation']['#finished'];
|
||||
unset($values['#translation']['#finished']);
|
||||
drupal_array_set_nested_value($data, array_merge($key, array('#translation')), $values['#translation']);
|
||||
if ($finished && (empty($values['#status']) || $values['#status'] == TMGMT_DATA_ITEM_STATE_PENDING)) {
|
||||
drupal_array_set_nested_value($data, array_merge($key, array('#status')), TMGMT_DATA_ITEM_STATE_TRANSLATED);
|
||||
}
|
||||
// Save the updated data structure.
|
||||
db_update('tmgmt_job_item')
|
||||
->condition('tjiid', $row->tjiid)
|
||||
->fields(array('data' => serialize($data)))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
$sandbox['progress']++;
|
||||
}
|
||||
|
||||
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
|
||||
if ($row) {
|
||||
$sandbox['last_tjiid'] = $row->tjiid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add word count column to {tmgmt_job_item}.
|
||||
*/
|
||||
function tmgmt_update_7005() {
|
||||
if (!db_field_exists('tmgmt_job_item', 'word_count')) {
|
||||
$spec = array(
|
||||
'description' => 'Word count of all texts contained in this job item.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
);
|
||||
db_add_field('tmgmt_job_item', 'word_count', $spec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data parsing helper function for tmgmt_update_7006().
|
||||
*
|
||||
* Copied from TMGMTJobItemController::count().
|
||||
*
|
||||
* @param $item
|
||||
* The current data item.
|
||||
* @param $entity
|
||||
* The job item the count should be calculated.
|
||||
*/
|
||||
function _tmgmt_data_count_7006($item, $entity) {
|
||||
if (!empty($item['#text'])) {
|
||||
if (_tmgmt_filter_data($item)) {
|
||||
module_load_include('module', 'tmgmt');
|
||||
// Count words of the data item.
|
||||
$entity->word_count += tmgmt_word_count($item['#text']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach (element_children($item) as $key) {
|
||||
_tmgmt_data_count_7006($item[$key], $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set word count for existing job items.
|
||||
*/
|
||||
function tmgmt_update_7006(&$sandbox) {
|
||||
if (!isset($sandbox['progress'])) {
|
||||
$sandbox['progress'] = 0;
|
||||
$sandbox['last_tjiid'] = 0;
|
||||
$sandbox['max'] = db_query('SELECT COUNT(tjiid) FROM {tmgmt_job_item}')->fetchField();
|
||||
}
|
||||
|
||||
$result = db_query('SELECT tjiid, data FROM {tmgmt_job_item} WHERE tjiid > :last_tjiid ORDER BY tjiid LIMIT 10', array(':last_tjiid' => $sandbox['last_tjiid']));
|
||||
foreach ($result as $row) {
|
||||
// Unseralize data, count it and then save the counters.
|
||||
if (!empty($row->data)) {
|
||||
$fake_job_item = (object)array(
|
||||
'word_count' => 0,
|
||||
);
|
||||
_tmgmt_data_count_7006(unserialize($row->data), $fake_job_item);
|
||||
db_update('tmgmt_job_item')
|
||||
->condition('tjiid', $row->tjiid)
|
||||
->fields((array)$fake_job_item)
|
||||
->execute();
|
||||
}
|
||||
$sandbox['progress']++;
|
||||
}
|
||||
|
||||
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
|
||||
if ($row) {
|
||||
$sandbox['last_tjiid'] = $row->tjiid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removing the tmgmt_auto_accept variable.
|
||||
*/
|
||||
function tmgmt_update_7007() {
|
||||
variable_del('tmgmt_auto_accept');
|
||||
}
|
||||
|
||||
/**
|
||||
* Introduce the reviewed counter and rename approved.
|
||||
*/
|
||||
function tmgmt_update_7008() {
|
||||
// Rename approved to reviewed to simplify the upgrade path.
|
||||
db_change_field('tmgmt_job_item', 'count_approved', 'count_reviewed', array(
|
||||
'description' => 'Counter for all reviewed data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
));
|
||||
// Add the accepted counter.
|
||||
db_add_field('tmgmt_job_item', 'count_accepted', array(
|
||||
'description' => 'Counter for all accepted data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data item status of accepted job items to accepted.
|
||||
*/
|
||||
function tmgmt_update_7009() {
|
||||
// First set count_accepted to the value of count_reviewed.
|
||||
db_update('tmgmt_job_item')
|
||||
->expression('count_accepted', 'count_reviewed + count_pending + count_translated')
|
||||
->condition('state', TMGMT_JOB_ITEM_STATE_ACCEPTED)
|
||||
->execute();
|
||||
// Then set count_reviewed to 0.
|
||||
db_update('tmgmt_job_item')
|
||||
->fields(array(
|
||||
'count_reviewed' => 0,
|
||||
'count_translated' => 0,
|
||||
'count_pending' => 0,
|
||||
))
|
||||
->condition('state', TMGMT_JOB_ITEM_STATE_ACCEPTED)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create tmgmt_remote table to provide generic functionality for remote job
|
||||
* mappings.
|
||||
*/
|
||||
function tmgmt_update_7010() {
|
||||
db_create_table('tmgmt_remote', array(
|
||||
'description' => 'TMGMT job remote mapping.',
|
||||
'fields' => array(
|
||||
'trid' => array(
|
||||
'description' => 'The primary key.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => '{tmgmt_job}.tjid foreign key',
|
||||
),
|
||||
'tjiid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => '{tmgmt_job_item}.tjjid foreign key',
|
||||
),
|
||||
'data_item_key' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Translation job data item key.',
|
||||
),
|
||||
'remote_identifier_1' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Custom remote identifier data.',
|
||||
),
|
||||
'remote_identifier_2' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Custom remote identifier data.',
|
||||
),
|
||||
'remote_identifier_3' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Custom remote identifier data.',
|
||||
),
|
||||
'remote_url' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Remote job url.',
|
||||
),
|
||||
'word_count' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Word count info provided by the remote service.',
|
||||
),
|
||||
'remote_data' => array(
|
||||
'description' => 'Custom remote data.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('trid'),
|
||||
'indexes' => array(
|
||||
'tjid' => array('tjid'),
|
||||
'tjiid' => array('tjiid'),
|
||||
'remote_identifiers' => array('remote_identifier_1', 'remote_identifier_2', 'remote_identifier_3'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add uid field for {tmgmt_message} table.
|
||||
*/
|
||||
function tmgmt_update_7011() {
|
||||
db_add_field('tmgmt_message', 'uid', array(
|
||||
'description' => 'The identifier of the user who performed the action.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortens remote_identifier_1/2 fields and recreates index for remote
|
||||
* identifier fields to include only the first two.
|
||||
*/
|
||||
function tmgmt_update_7012() {
|
||||
db_drop_index('tmgmt_remote', 'remote_identifiers');
|
||||
db_change_field('tmgmt_remote', 'remote_identifier_1', 'remote_identifier_1', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 127,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Custom remote identifier data.',
|
||||
));
|
||||
db_change_field('tmgmt_remote', 'remote_identifier_2', 'remote_identifier_2', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 127,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Custom remote identifier data.',
|
||||
));
|
||||
db_add_index('tmgmt_remote', 'remote_identifiers', array('remote_identifier_1', 'remote_identifier_2'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortens reference and label fields in {tmgmt_job} to length 255.
|
||||
*
|
||||
* Avoids issues with InnoDB and UTF-8 key limits.
|
||||
*/
|
||||
function tmgmt_update_7013() {
|
||||
db_drop_index('tmgmt_job', 'reference');
|
||||
db_change_field('tmgmt_job', 'reference', 'reference', array(
|
||||
'description' => 'Remote identifier of this translation job.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
));
|
||||
db_change_field('tmgmt_job', 'label', 'label', array(
|
||||
'description' => 'Optional user provided label of the job.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
));
|
||||
db_add_index('tmgmt_job', 'reference', array('reference'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add amount and currency fields for {tmgmt_remote} table.
|
||||
*/
|
||||
function tmgmt_update_7014() {
|
||||
db_add_field('tmgmt_remote', 'amount', array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Amount charged for the remote translation job.',
|
||||
));
|
||||
db_add_field('tmgmt_remote', 'currency', array(
|
||||
'type' => 'char',
|
||||
'length' => 3,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Amount charged currency.',
|
||||
));
|
||||
}
|
1550
sites/all/modules/contrib/localisation/tmgmt/tmgmt.module
Normal file
1550
sites/all/modules/contrib/localisation/tmgmt/tmgmt.module
Normal file
File diff suppressed because it is too large
Load Diff
247
sites/all/modules/contrib/localisation/tmgmt/tmgmt.rules.inc
Normal file
247
sites/all/modules/contrib/localisation/tmgmt/tmgmt.rules.inc
Normal file
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Rules integration.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function tmgmt_rules_action_info() {
|
||||
$info['tmgmt_rules_job_request_translation'] = array(
|
||||
'label' => t('Request Job translation'),
|
||||
'group' => t('Translation Management'),
|
||||
'parameter' => array(
|
||||
'job' => array(
|
||||
'type' => 'tmgmt_job',
|
||||
'label' => t('Translation Job'),
|
||||
'description' => t('The translation job for which translations should be requested.'),
|
||||
),
|
||||
),
|
||||
'access callback' => 'tmgmt_rules_job_submit_access',
|
||||
);
|
||||
$info['tmgmt_rules_job_accept_translation'] = array(
|
||||
'label' => t('Accept Job translation'),
|
||||
'group' => t('Translation Management'),
|
||||
'parameter' => array(
|
||||
'job' => array(
|
||||
'type' => 'tmgmt_job',
|
||||
'label' => t('Translation Job'),
|
||||
'description' => t('The translation job for which translations should be accepted.'),
|
||||
),
|
||||
'message' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('An optional message'),
|
||||
'description' => t('Will be stored in the job message and displayed to the user.'),
|
||||
'optional' => TRUE,
|
||||
),
|
||||
),
|
||||
'access callback' => 'tmgmt_rules_job_accept_translation_access',
|
||||
);
|
||||
$info['tmgmt_rules_job_abort_translation'] = array(
|
||||
'label' => t('Abort translation job'),
|
||||
'group' => t('Translation Management'),
|
||||
'parameter' => array(
|
||||
'job' => array(
|
||||
'type' => 'tmgmt_job',
|
||||
'label' => t('Translation Job'),
|
||||
'description' => t('The translation job that should be aborted.'),
|
||||
),
|
||||
),
|
||||
'access callback' => 'tmgmt_rules_job_submit_access',
|
||||
);
|
||||
$info['tmgmt_rules_job_delete'] = array(
|
||||
'label' => t('Delete Job'),
|
||||
'group' => t('Translation Management'),
|
||||
'parameter' => array(
|
||||
'job' => array(
|
||||
'type' => 'tmgmt_job',
|
||||
'label' => t('Translation Job'),
|
||||
'description' => t('The translation job that should be deleted.'),
|
||||
),
|
||||
),
|
||||
'access callback' => 'tmgmt_rules_job_delete_access',
|
||||
);
|
||||
$info['tmgmt_rules_job_checkout'] = array(
|
||||
'label' => t('Checkout a job'),
|
||||
'group' => t('Translation Management'),
|
||||
'parameter' => array(
|
||||
'job' => array(
|
||||
'type' => 'tmgmt_job',
|
||||
'label' => t('Translation Job'),
|
||||
'description' => t('The translation job that should be checked out.'),
|
||||
),
|
||||
),
|
||||
'access callback' => 'tmgmt_rules_job_submit_access',
|
||||
);
|
||||
$info['tmgmt_get_first_from_node_list'] = array(
|
||||
'label' => t('Get first item from a list of nodes'),
|
||||
'group' => t('Data'),
|
||||
'parameter' => array(
|
||||
'list' => array(
|
||||
'type' => 'list<node>',
|
||||
'label' => t('List'),
|
||||
'restriction' => 'selector',
|
||||
),
|
||||
),
|
||||
'provides' => array(
|
||||
'first_node' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Node'),
|
||||
),
|
||||
),
|
||||
);
|
||||
$info['tmgmt_rules_create_job'] = array(
|
||||
'label' => t('Create a job for a given source language'),
|
||||
'group' => t('Translation Management'),
|
||||
'parameter' => array(
|
||||
'source_language' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Source Language'),
|
||||
'description' => t('The language from which should be translated'),
|
||||
'options list' => 'entity_metadata_language_list',
|
||||
),
|
||||
),
|
||||
'provides' => array(
|
||||
'job' => array(
|
||||
'label' => t('Job'),
|
||||
'type' => 'tmgmt_job',
|
||||
),
|
||||
),
|
||||
);
|
||||
$info['tmgmt_rules_job_add_item'] = array(
|
||||
'label' => t('Add an item to a job'),
|
||||
'group' => t('Translation Management'),
|
||||
'parameter' => array(
|
||||
'job' => array(
|
||||
'type' => 'tmgmt_job',
|
||||
'label' => t('Translation Job'),
|
||||
'description' => t('The translation job that should be canceled.'),
|
||||
),
|
||||
'plugin' => array(
|
||||
'type' => 'token',
|
||||
'label' => t('Source plugin'),
|
||||
'description' => t('The source plugin of this item'),
|
||||
//'options list' => 'entity_metadata_language_list',
|
||||
),
|
||||
'item_type' => array(
|
||||
'type' => 'token',
|
||||
'label' => t('Item type'),
|
||||
'description' => t('The item type'),
|
||||
//'options list' => 'entity_metadata_language_list',
|
||||
),
|
||||
'item_id' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Item ID'),
|
||||
'description' => t('ID of the referenced item'),
|
||||
),
|
||||
),
|
||||
);
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules callback to request a translation of a job.
|
||||
*/
|
||||
function tmgmt_rules_job_request_translation(TMGMTJob $job) {
|
||||
if ($job->isTranslatable()) {
|
||||
$job->requestTranslation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules callback to accept a translation of a job.
|
||||
*/
|
||||
function tmgmt_rules_job_accept_translation(TMGMTJob $job, $message) {
|
||||
foreach ($job->getItems() as $item) {
|
||||
if ($item->isNeedsReview()) {
|
||||
$item->acceptTranslation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules callback to cancel a translation job.
|
||||
*/
|
||||
function tmgmt_rules_job_abort_translation(TMGMTJob $job) {
|
||||
$job->abortTranslation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules callback to redirect to a translation job.
|
||||
*/
|
||||
function tmgmt_rules_job_checkout(TMGMTJob $job) {
|
||||
$redirects = tmgmt_ui_job_checkout_multiple(array($job));
|
||||
// If necessary, do a redirect.
|
||||
if ($redirects) {
|
||||
tmgmt_ui_redirect_queue_set($redirects, current_path());
|
||||
drupal_goto(tmgmt_ui_redirect_queue_dequeue());
|
||||
|
||||
// Count of the job messages is one less due to the final redirect.
|
||||
drupal_set_message(format_plural(count($redirects), t('One job needs to be checked out.'), t('@count jobs need to be checked out.')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules callback to get the job for a specific language combination.
|
||||
*/
|
||||
function tmgmt_rules_create_job($source_language) {
|
||||
return array(
|
||||
'job' => tmgmt_job_create($source_language, ''),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules callback to add an item to a job.
|
||||
*/
|
||||
function tmgmt_rules_job_add_item(TMGMTJob $job, $plugin, $item_type, $item_id) {
|
||||
try {
|
||||
$job->addItem($plugin, $item_type, $item_id);
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
watchdog_exception('tmgmt', $e);
|
||||
drupal_set_message(t('Unable to add job item of type %type with id %id. Make sure the source content is not empty.',
|
||||
array('%type' => $item_type, '%id' => $item_id)), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules action to extract the first node from a node list.
|
||||
*/
|
||||
function tmgmt_get_first_from_node_list($list) {
|
||||
return array(
|
||||
'first_node' => reset($list),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules action to delete a translation job.
|
||||
*/
|
||||
function tmgmt_rules_job_delete(TMGMTJob $job) {
|
||||
// Prevent users without job delete permission to be able to delete jobs.
|
||||
if (tmgmt_job_access('delete')) {
|
||||
$job->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to rules job delete action.
|
||||
*/
|
||||
function tmgmt_rules_job_delete_access() {
|
||||
return tmgmt_job_access('delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to rules job submit like actions.
|
||||
*/
|
||||
function tmgmt_rules_job_submit_access() {
|
||||
return tmgmt_job_access('submit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to rules accept translation action.
|
||||
*/
|
||||
function tmgmt_rules_job_accept_translation_access() {
|
||||
return tmgmt_job_access('accept');
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user