1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7//
8// TranslationController manages translations of objects (for example translations of a wiki page)
9
10class Services_Language_TranslationController
11{
12	private $utilities;
13
14	function __construct()
15	{
16		$this->utilities = new Services_Language_Utilities;
17	}
18
19	function setUp()
20	{
21		global $prefs;
22
23		if ($prefs['feature_multilingual'] != 'y') {
24			throw new Services_Exception(tr('Feature Disabled'), 403);
25		}
26	}
27
28	/**
29	 * List translations relation of an instance of an object type (eg: translations of a wiki page)
30	 *
31	 * @param $input
32	 *
33	 * @return array Array of objects linked together as translations of each other
34	 *
35	 * @throws Services_Exception
36	 */
37	function action_manage($input)
38	{
39		$type = $input->type->text();
40		$objectFilter = $this->getObjectFilter($type);
41
42		if (! $objectFilter) {
43			throw new Services_Exception(tr('Translation not supported for the specified object type'), 400);
44		}
45
46		$object = $input->source->$objectFilter();
47
48		if (! $object) {
49			throw new Services_Exception(tr('No source provided'), 400);
50		}
51
52		return [
53			'title' => tr('Manage translations'),
54			'type' => $type,
55			'source' => $object,
56			'filters' => $this->getSearchFilters($type, $object),
57			'translations' => $this->utilities->getTranslations($type, $object),
58			'canAttach' => $this->canAttach($type, $object),
59			'canDetach' => $this->canDetach($type, $object),
60		];
61	}
62
63	/**
64	 * Attach (link) translations for objects (eg: wiki pages)
65	 *
66	 * @param $input Instances of object types, eg: wiki pages
67	 *
68	 * @return Forward to utility action to perform the attaching
69	 *
70	 * @throws Services_Exception
71	 */
72	function action_attach($input)
73	{
74		$type = $input->type->text();
75		$objectFilter = $this->getObjectFilter($type);
76
77		if (! $objectFilter) {
78			throw new Services_Exception(tr('Translation not supported for the specified object type'), 400);
79		}
80
81		$source = $input->source->$objectFilter();
82		$target = $input->target->none();
83		$target = end(explode(':', $target, 2));
84		$target = TikiFilter::get($objectFilter)->filter($target);
85
86		if (! $source || ! $target) {
87			throw new Services_Exception(tr('No source or target provided'), 400);
88		}
89
90		if (! $this->canAttach($type, $source) || ! $this->canAttach($type, $target)) {
91			throw new Services_Exception(tr('You do not have permission to attach the selected translations'), 403);
92		}
93
94		$succeeded = $this->utilities->insertTranslation($type, $source, $target);
95
96		if (! $succeeded) {
97			throw new Services_Exception(tr('Could not attach the translations.'), 409);
98		}
99
100		return [
101			'FORWARD' => [
102				'action' => 'manage',
103				'type' => $type,
104				'source' => $source,
105			],
106		];
107	}
108
109	/**
110	 * Detach (unlink) translations for objects (eg: wiki pages)
111	 *
112	 * @param $input Instances of object types, eg: wiki pages
113	 *
114	 * @return Forward to utility action to perform the deattaching
115	 *
116	 * @throws Services_Exception
117	 */
118	function action_detach($input)
119	{
120		$type = $input->type->text();
121		$objectFilter = $this->getObjectFilter($type);
122		$confirmed = $input->confirm->int();
123
124		if (! $objectFilter) {
125			throw new Services_Exception(tr('Translation not supported for the specified object type'), 400);
126		}
127
128		$source = $input->source->$objectFilter();
129		$target = $input->target->$objectFilter();
130
131		if (! $source || ! $target) {
132			throw new Services_Exception(tr('No source or target provided'), 400);
133		}
134
135		if (! $this->canDetach($type, $source) || ! $this->canDetach($type, $target)) {
136			throw new Services_Exception(tr('You do not have permission to detach the selected translations'), 403);
137		}
138
139		if (! $confirmed) {
140			return [
141				'title' => tr('Manage translations'),
142				'type' => $type,
143				'source' => $source,
144				'target' => $target,
145			];
146		}
147
148		$this->utilities->detachTranslation($type, $source, $target);
149
150		return [
151			'FORWARD' => [
152				'action' => 'manage',
153				'type' => $type,
154				'source' => $source,
155			],
156		];
157	}
158
159	/**
160	 * Machine translation of an object (eg: a wiki page)
161	 *
162	 * @param $input An object, eg: a wiki page
163	 *
164	 * @return action Forward to utility action to perform the attaching
165	 */
166	function action_translate($input)
167	{
168		Services_Exception_Disabled::check('feature_machine_translation');
169
170		global $prefs;
171
172		$content = $input->content->rawhtml_unsafe();
173		if (! empty($input->lang->text())) {
174			$lang = $input->lang->text();
175		} else {
176			$lang = $prefs['language'];
177		}
178
179		$factory = new Multilingual_MachineTranslation;
180		$impl = $factory->getDetectImplementation($lang);
181
182		$content = $impl->translateText($content);
183
184		return [
185			'content' => $content,
186			'target' => $lang
187		];
188	}
189
190	private function getObjectFilter($type)
191	{
192		switch ($type) {
193			case 'wiki page':
194				return 'pagename';
195			case 'article':
196			case 'trackeritem':
197				return 'int';
198		}
199	}
200
201	private function getSearchFilters($type, $object)
202	{
203		$translations = $this->utilities->getTranslations($type, $object);
204		$langLib = TikiLib::lib('language');
205		$languages = $langLib->get_language_map();
206
207		foreach ($translations as $trans) {
208			unset($languages[$trans['lang']]);
209		}
210
211		unset($languages[$this->utilities->getLanguage($type, $object)]);
212
213		$language = '"' . implode('" OR "', array_keys($languages)) . '"';
214		if ($language == '""') {
215			$language = null;
216		}
217
218		$filters = [
219			'type' => $type,
220			'language' => $language,
221		];
222
223		if ($type == 'trackeritem') {
224			$info = TikiLib::lib('trk')->get_tracker_item($object);
225			$filters['tracker_id'] = $info['trackerId'];
226		}
227
228		return $filters;
229	}
230
231	/**
232	 * Private function to determine if user is allowed to attach (link) translations for objects (eg: wiki pages)
233	 *
234	 * @param string $type object type, eg: wiki page
235	 * @param int $object an instance of object type, eg: a wiki page
236	 *
237	 * @return
238	 */
239	private function canAttach($type, $object)
240	{
241		global $prefs, $user;
242		$perms = Perms::get($type, $object);
243
244		if ($type == 'wiki page' && $perms->edit) {
245			return true;
246		}
247
248		if ($type == 'article' && $perms->edit_article) {
249			return true;
250		}
251
252		if ($type == 'wiki page' && $prefs['wiki_creator_admin'] == 'y' && $user) {
253			$info = TikiLib::lib('tiki')->get_page_info($object);
254			return $info['creator'] == $user;
255		}
256
257		if ($type == 'article' && $user) {
258			$artlib = TikiLib::lib('art');
259			$info = $artlib->get_article($object);
260			return $info['author'] == $user && $info['creator_edit'] == 'y';
261		}
262
263		return $perms->admin;
264	}
265
266	/**
267	 * Private function to determine if user is allowed to detach (unlink) translations for objects (eg: wiki pages)
268	 *
269	 * @param string $type object type, eg: wiki page
270	 * @param int $object an instance of object type, eg: a wiki page
271	 *
272	 * @return
273	 */
274	private function canDetach($type, $object)
275	{
276		$perms = Perms::get($type, $object);
277		return $perms->detach_translation;
278	}
279}
280