├── ScriptoPlugin.php
├── build.xml
├── controllers
└── IndexController.php
├── languages
└── template.base.pot
├── libraries
├── Scripto.php
└── Scripto
│ ├── Adapter
│ ├── Example.php
│ ├── Exception.php
│ └── Interface.php
│ ├── Document.php
│ ├── Exception.php
│ └── Service
│ ├── Exception.php
│ └── MediaWiki.php
├── models
└── ScriptoAdapterOmeka.php
├── plugin.ini
├── routes.ini
└── views
├── admin
└── plugins
│ └── scripto-config-form.php
└── shared
├── index
├── diff.php
├── history.php
├── index.php
├── login.php
├── recent-changes.php
├── revision.php
├── transcribe.php
└── watchlist.php
└── javascripts
├── OpenLayers.js
├── img
├── east-mini.png
├── north-mini.png
├── south-mini.png
├── west-mini.png
├── zoom-minus-mini.png
├── zoom-plus-mini.png
└── zoom-world-mini.png
└── theme
└── default
└── style.css
/ScriptoPlugin.php:
--------------------------------------------------------------------------------
1 | array(
43 | // gif
44 | 'image/gif', 'image/x-xbitmap', 'image/gi_',
45 | // jpg
46 | 'image/jpeg', 'image/jpg', 'image/jpe_', 'image/pjpeg',
47 | 'image/vnd.swiftview-jpeg',
48 | // png
49 | 'image/png', 'application/png', 'application/x-png',
50 | // bmp
51 | 'image/bmp', 'image/x-bmp', 'image/x-bitmap',
52 | 'image/x-xbitmap', 'image/x-win-bitmap',
53 | 'image/x-windows-bmp', 'image/ms-bmp', 'image/x-ms-bmp',
54 | 'application/bmp', 'application/x-bmp',
55 | 'application/x-win-bitmap',
56 | ),
57 | 'fileExtensions' => array(
58 | 'gif', 'jpeg', 'jpg', 'jpe', 'png', 'bmp',
59 | ),
60 | );
61 |
62 | /**
63 | * @var MIME types compatible with Google Docs viewer.
64 | */
65 | public static $fileIdentifiersGoogleDocs = array(
66 | 'mimeTypes' => array(
67 | // pdf
68 | 'application/pdf', 'application/x-pdf',
69 | 'application/acrobat', 'applications/vnd.pdf', 'text/pdf',
70 | 'text/x-pdf',
71 | // docx
72 | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
73 | // doc
74 | 'application/msword', 'application/doc', 'appl/text',
75 | 'application/vnd.msword', 'application/vnd.ms-word',
76 | 'application/winword', 'application/word', 'application/vnd.ms-office',
77 | 'application/x-msw6', 'application/x-msword',
78 | // ppt
79 | 'application/vnd.ms-powerpoint', 'application/mspowerpoint',
80 | 'application/ms-powerpoint', 'application/mspowerpnt',
81 | 'application/vnd-mspowerpoint', 'application/powerpoint',
82 | 'application/x-powerpoint', 'application/x-m',
83 | // pptx
84 | 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
85 | // xls
86 | 'application/vnd.ms-excel', 'application/msexcel',
87 | 'application/x-msexcel', 'application/x-ms-excel',
88 | 'application/vnd.ms-excel', 'application/x-excel',
89 | 'application/x-dos_ms_excel', 'application/xls',
90 | // xlsx
91 | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
92 | // tiff
93 | 'image/tiff',
94 | // ps, ai
95 | 'application/postscript', 'application/ps',
96 | 'application/x-postscript', 'application/x-ps',
97 | 'text/postscript', 'application/x-postscript-not-eps',
98 | // eps
99 | 'application/eps', 'application/x-eps', 'image/eps',
100 | 'image/x-eps',
101 | // psd
102 | 'image/vnd.adobe.photoshop', 'image/photoshop',
103 | 'image/x-photoshop', 'image/psd', 'application/photoshop',
104 | 'application/psd', 'zz-application/zz-winassoc-psd',
105 | // dxf
106 | 'application/dxf', 'application/x-autocad',
107 | 'application/x-dxf', 'drawing/x-dxf', 'image/vnd.dxf',
108 | 'image/x-autocad', 'image/x-dxf',
109 | 'zz-application/zz-winassoc-dxf',
110 | // xvg
111 | 'image/svg+xml',
112 | // xps
113 | 'application/vnd.ms-xpsdocument',
114 | ),
115 | 'fileExtensions' => array(
116 | 'pdf',
117 | 'docx',
118 | 'doc', 'dot',
119 | 'ppt', 'pps', 'pot',
120 | 'pptx',
121 | 'xls', 'xlm', 'xla', 'xlc', 'xlt', 'xlw',
122 | 'xlsx',
123 | 'tiff', 'tif',
124 | 'ai', 'eps', 'ps',
125 | 'psd',
126 | 'dxf',
127 | 'xvg',
128 | 'xps',
129 | ),
130 | );
131 |
132 | /**
133 | * Initialize Scripto.
134 | */
135 | public function hookInitialize()
136 | {
137 | // Add translation.
138 | add_translation_source(dirname(__FILE__) . '/languages');
139 | }
140 |
141 | /**
142 | * Install Scripto.
143 | */
144 | public function hookInstall()
145 | {
146 | // Don't install if an element set by the name "Scripto" already exists.
147 | if ($this->_db->getTable('ElementSet')->findByName(self::ELEMENT_SET_NAME)) {
148 | throw new Omeka_Plugin_Installer_Exception(
149 | __('An element set by the name "%s" already exists. You must delete '
150 | . 'that element set to install this plugin.', self::ELEMENT_SET_NAME)
151 | );
152 | }
153 |
154 | $elementSetMetadata = array('name' => self::ELEMENT_SET_NAME);
155 | $elements = array(
156 | array('name' => 'Transcription',
157 | 'description' => 'A written representation of a document.')
158 | );
159 | insert_element_set($elementSetMetadata, $elements);
160 | }
161 |
162 | /**
163 | * Uninstall Scripto.
164 | */
165 | public function hookUninstall()
166 | {
167 | // Delete the Scripto element set.
168 | $this->_db->getTable('ElementSet')->findByName(self::ELEMENT_SET_NAME)->delete();
169 |
170 | // Delete options that are specific to Scripto.
171 | delete_option('scripto_mediawiki_api_url');
172 | delete_option('scripto_image_viewer');
173 | delete_option('scripto_use_google_docs_viewer');
174 | delete_option('scripto_import_type');
175 | delete_option('scripto_home_page_text');
176 | }
177 |
178 | /**
179 | * Appends a warning message to the uninstall confirmation page.
180 | */
181 | public function hookUninstallMessage()
182 | {
183 | echo '
' . __(
184 | '%1$sWarning%2$s: This will permanently delete the "%3$s" element set and '
185 | . 'all transcriptions imported from MediaWiki. You may deactivate this '
186 | . 'plugin if you do not want to lose data. Uninstalling this plugin will '
187 | . 'not affect your MediaWiki database in any way.',
188 | '', ' ', self::ELEMENT_SET_NAME) . '
';
189 | }
190 |
191 | /**
192 | * Define routes.
193 | *
194 | * @param Zend_Controller_Router_Rewrite $router
195 | */
196 | public function hookDefineRoutes($args)
197 | {
198 | $args['router']->addConfig(new Zend_Config_Ini(dirname(__FILE__) . '/routes.ini', 'routes'));
199 | }
200 |
201 | /**
202 | * Render the config form.
203 | */
204 | public function hookConfigForm()
205 | {
206 | // Set form defaults.
207 | $imageViewer = get_option('scripto_image_viewer');
208 | if (!in_array($imageViewer, array('openlayers'))) {
209 | $imageViewer = 'default';
210 | }
211 | $useGoogleDocsViewer = get_option('scripto_use_google_docs_viewer');
212 | if (is_null($useGoogleDocsViewer)) {
213 | $useGoogleDocsViewer = 0;
214 | }
215 | $importType = get_option('scripto_import_type');
216 | if (is_null($importType)) {
217 | $importType = 'html';
218 | }
219 |
220 | echo get_view()->partial(
221 | 'plugins/scripto-config-form.php',
222 | array('image_viewer' => $imageViewer,
223 | 'use_google_docs_viewer' => $useGoogleDocsViewer,
224 | 'import_type' => $importType)
225 | );
226 | }
227 |
228 | /**
229 | * Handle a submitted config form.
230 | */
231 | public function hookConfig()
232 | {
233 | // Validate the MediaWiki API URL.
234 | if (!Scripto::isValidApiUrl($_POST['scripto_mediawiki_api_url'])) {
235 | throw new Omeka_Plugin_Installer_Exception('Invalid MediaWiki API URL');
236 | }
237 |
238 | // Set options that are specific to Scripto.
239 | set_option('scripto_mediawiki_api_url', $_POST['scripto_mediawiki_api_url']);
240 | set_option('scripto_mediawiki_cookie_prefix', $_POST['scripto_mediawiki_cookie_prefix']);
241 | set_option('scripto_image_viewer', $_POST['scripto_image_viewer']);
242 | set_option('scripto_use_google_docs_viewer', $_POST['scripto_use_google_docs_viewer']);
243 | set_option('scripto_import_type', $_POST['scripto_import_type']);
244 | set_option('scripto_home_page_text', $_POST['scripto_home_page_text']);
245 | }
246 |
247 |
248 | /**
249 | * Append the transcribe link to the public items show page.
250 | */
251 | public function hookPublicItemsShow()
252 | {
253 | $this->_appendToItemsShow();
254 | }
255 |
256 | /**
257 | * Append the transcribe link to the admin items show page.
258 | */
259 | public function hookAdminItemsShow()
260 | {
261 | $this->_appendToItemsShow();
262 | }
263 |
264 | /**
265 | * Add Scripto to the admin navigation.
266 | *
267 | * @param array $nav
268 | * @return array
269 | */
270 | public function filterAdminNavigationMain($nav)
271 | {
272 | $nav[] = array('label' => __('Scripto'), 'uri' => url('scripto'));
273 | return $nav;
274 | }
275 |
276 | /**
277 | * Add Scripto to the public navigation.
278 | *
279 | * @param array $nav
280 | * @return array
281 | */
282 | public function filterPublicNavigationMain($nav)
283 | {
284 | $nav[] = array('label' => __('Scripto'), 'uri' => url('scripto'));
285 | return $nav;
286 | }
287 |
288 | /**
289 | * Append the transcribe link to the items show page.
290 | */
291 | protected function _appendToItemsShow()
292 | {
293 | $item = get_current_record('item');
294 | $scripto = self::getScripto();
295 | // Do not show page links if document is not valid.
296 | if (!$scripto->documentExists($item->id)) {
297 | return;
298 | }
299 | $doc = $scripto->getDocument($item->id);
300 | ?>
301 |
302 |
303 | getPages() as $pageId => $pageName): ?>
304 |
308 |
309 |
310 | getWebPath('original');
322 | $imageSize = ScriptoPlugin::getImageSize($imageUrl, 250);
323 |
324 | ?>
325 |
338 |
339 | setQuery(array('url' => $file->getWebPath('original'),
352 | 'embedded' => 'true'));
353 | echo '';
354 | }
355 |
356 | /**
357 | * Convenience method to get the Scripto object.
358 | *
359 | * @param string $apiUrl
360 | */
361 | public static function getScripto($apiUrl = null)
362 | {
363 | if (null === $apiUrl) {
364 | $apiUrl = get_option('scripto_mediawiki_api_url');
365 | }
366 | $cookiePrefix = get_option('scripto_mediawiki_cookie_prefix');
367 |
368 | return new Scripto(new ScriptoAdapterOmeka, array(
369 | 'api_url' => $apiUrl,
370 | 'cookie_prefix' => $cookiePrefix ? $cookiePrefix : null,
371 | ));
372 | }
373 |
374 | /**
375 | * Return a truncated string with left and right padding.
376 | *
377 | * Primarily used for truncating long document page names that would
378 | * otherwise break tables.
379 | *
380 | * @param string $str The string to truncate.
381 | * @param int $length The trancate length.
382 | * @param string $default The string to return if the string is empty.
383 | * @return string
384 | */
385 | public static function truncate($str, $length, $default = '')
386 | {
387 | $str = trim($str);
388 | if (empty($str)) {
389 | return $default;
390 | }
391 | if (strlen($str) <= $length) {
392 | return $str;
393 | }
394 | $padding = floor($length / 2);
395 | return preg_replace('/^(.{' . $padding . '}).*(.{' . $padding . '})$/', '$1... $2', $str);
396 | }
397 |
398 | /**
399 | * Get dimensions of the provided image.
400 | *
401 | * @param string $filename URI to file.
402 | * @param int $width Width constraint.
403 | * @return array
404 | */
405 | public static function getImageSize($filename, $width = null)
406 | {
407 | $size = getimagesize($filename);
408 | if (!$size) {
409 | return false;
410 | }
411 | if (is_int($width)) {
412 | $height = round(($width * $size[1]) / $size[0]);
413 | } else {
414 | $width = $size[1];
415 | $height = $size[0];
416 | }
417 | return array('width' => $width, 'height' => $height);
418 | }
419 | }
420 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/controllers/IndexController.php:
--------------------------------------------------------------------------------
1 | getRequest()->getActionName()) {
15 |
16 | // Image viewers.
17 | switch (get_option('scripto_image_viewer')) {
18 | case 'openlayers':
19 | add_file_display_callback(ScriptoPlugin::$fileIdentifiersOpenLayers, 'ScriptoPlugin::openLayers');
20 | break;
21 | default:
22 | // Do nothing. Use Omeka default file display stategy.
23 | break;
24 | }
25 |
26 | // Google Docs viewer.
27 | if (get_option('scripto_use_google_docs_viewer')) {
28 | add_file_display_callback(ScriptoPlugin::$fileIdentifiersGoogleDocs, 'ScriptoPlugin::googleDocs');
29 | }
30 | }
31 | }
32 |
33 | /**
34 | * View document pages to which you have contributed.
35 | */
36 | public function indexAction()
37 | {
38 | try {
39 | $scripto = ScriptoPlugin::getScripto();
40 | $documentPages = array();
41 | // Don't bother getting the user document pages if not logged in.
42 | if ($scripto->isLoggedIn()) {
43 | $documentPages = $scripto->getUserDocumentPages(500);
44 | }
45 | } catch (Scripto_Exception $e) {
46 | $this->_helper->flashMessenger($e->getMessage());
47 | }
48 |
49 | $this->view->scripto = $scripto;
50 | $this->view->documentPages = $documentPages;
51 | $this->view->homePageText = trim(get_option('scripto_home_page_text'));
52 | }
53 |
54 | /**
55 | * Log in to Scripto.
56 | */
57 | public function loginAction()
58 | {
59 | try {
60 | $scripto = ScriptoPlugin::getScripto();
61 | // Handle a login.
62 | if ($this->_getParam('scripto_mediawiki_login')) {
63 | $scripto->login($this->_getParam('scripto_mediawiki_username'),
64 | $this->_getParam('scripto_mediawiki_password'));
65 | $this->_helper->flashMessenger(__('Successfully logged into Scripto.'), 'success');
66 | }
67 | // Redirect if logged in.
68 | if ($scripto->isLoggedIn()) {
69 | if ($this->_getParam('scripto_redirect_url')) {
70 | $this->_helper->redirector->gotoUrl($this->_getParam('scripto_redirect_url'));
71 | } else {
72 | $this->_helper->redirector->goto('index');
73 | }
74 | }
75 | } catch (Scripto_Service_Exception $e) {
76 | $this->_helper->flashMessenger($e->getMessage());
77 | }
78 |
79 | // Set the URL to redirect to on a sucessful login.
80 | $redirectUrl = null;
81 | if ($this->_getParam('scripto_redirect_url')) {
82 | // Assume login error and reassign the parameter.
83 | $redirectUrl = $this->_getParam('scripto_redirect_url');
84 | } else if ('scripto' == $this->getRequest()->getModuleName() && $_SERVER['HTTP_REFERER']) {
85 | // Assign HTTP referer to scripto_redirect_url parameter only if
86 | // coming from the Scripto application.
87 | $redirectUrl = $_SERVER['HTTP_REFERER'];
88 | }
89 |
90 | $this->view->redirectUrl = $redirectUrl;
91 | $this->view->scripto = $scripto;
92 | }
93 |
94 | /**
95 | * Log out of Scripto.
96 | */
97 | public function logoutAction()
98 | {
99 | try {
100 | $scripto = ScriptoPlugin::getScripto();
101 | $scripto->logout();
102 | $this->_helper->flashMessenger(__('Successfully logged out of Scripto.'), 'success');
103 | } catch (Scripto_Exception $e) {
104 | $this->_helper->flashMessenger($e->getMessage());
105 | }
106 |
107 | // Always redirect.
108 | $this->_helper->redirector->goto('index');
109 | }
110 |
111 | /**
112 | * View your watchlist.
113 | */
114 | public function watchlistAction()
115 | {
116 | try {
117 | $scripto = ScriptoPlugin::getScripto();
118 | // Anonymous users
119 | if (!$scripto->isLoggedIn()) {
120 | $this->_helper->redirector->goto('index');
121 | }
122 | $watchlist = $scripto->getWatchlist(500);
123 | } catch (Scripto_Exception $e) {
124 | $this->_helper->flashMessenger($e->getMessage());
125 | }
126 |
127 | $this->view->scripto = $scripto;
128 | $this->view->watchlist = $watchlist;
129 | }
130 |
131 | /**
132 | * View recent changes to the document pages.
133 | */
134 | public function recentChangesAction()
135 | {
136 | try {
137 | $scripto = ScriptoPlugin::getScripto();
138 | $recentChanges = $scripto->getRecentChanges(500);
139 | } catch (Scripto_Exception $e) {
140 | $this->_helper->flashMessenger($e->getMessage());
141 | }
142 |
143 | $this->view->scripto = $scripto;
144 | $this->view->recentChanges = $recentChanges;
145 | }
146 |
147 | /**
148 | * View transcription interface.
149 | */
150 | public function transcribeAction()
151 | {
152 | try {
153 | $scripto = ScriptoPlugin::getScripto();
154 | $doc = $scripto->getDocument($this->_getParam('item-id'));
155 | $doc->setPage($this->_getParam('file-id'));
156 |
157 | // Set the File object.
158 | $file = $this->_helper->db->getTable('File')->find($doc->getPageId());
159 |
160 | // Set the page HTML.
161 | $transcriptionPageHtml = Scripto::removeHtmlAttributes($doc->getTranscriptionPageHtml());
162 | $talkPageHtml = Scripto::removeHtmlAttributes($doc->getTalkPageHtml());
163 |
164 | // Set all the document's pages.
165 | $pages = $doc->getPages();
166 |
167 | // Set the pagination.
168 | $paginationUrls = array();
169 | foreach ($pages as $pageId => $pageName) {
170 | if (isset($current)) {
171 | $paginationUrls['next'] = $this->view->url(array(
172 | 'action' => 'transcribe',
173 | 'item-id' => $doc->getId(),
174 | 'file-id' => $pageId
175 | ), 'scripto_action_item_file');
176 | break;
177 | }
178 | if ($pageId == $doc->getPageId()) {
179 | $current = true;
180 | } else {
181 | $paginationUrls['previous'] = $this->view->url(array(
182 | 'action' => 'transcribe',
183 | 'item-id' => $doc->getId(),
184 | 'file-id' => $pageId
185 | ), 'scripto_action_item_file');
186 | }
187 | }
188 |
189 | } catch (Scripto_Exception $e) {
190 | $this->_helper->flashMessenger($e->getMessage());
191 | $this->_helper->redirector->goto('index');
192 | }
193 |
194 | $this->view->file = $file;
195 | $this->view->transcriptionPageHtml = $transcriptionPageHtml;
196 | $this->view->talkPageHtml = $talkPageHtml;
197 | $this->view->paginationUrls = $paginationUrls;
198 | $this->view->scripto = $scripto;
199 | $this->view->doc = $doc;
200 | }
201 |
202 | /**
203 | * View page history.
204 | */
205 | public function historyAction()
206 | {
207 | try {
208 | $scripto = ScriptoPlugin::getScripto();
209 | $doc = $scripto->getDocument($this->_getParam('item-id'));
210 | $doc->setPage($this->_getParam('file-id'));
211 |
212 | // Set the history depending on namespace index.
213 | if (1 == $this->_getParam('namespace-index')) {
214 | $info = $doc->getTalkPageInfo();
215 | $history = $doc->getTalkPageHistory(100);
216 | } else {
217 | $info = $doc->getTranscriptionPageInfo();
218 | $history = $doc->getTranscriptionPageHistory(100);
219 | }
220 | } catch (Scripto_Exception $e) {
221 | $this->_helper->flashMessenger($e->getMessage());
222 | $this->_helper->redirector->goto('index');
223 | }
224 |
225 | $this->view->scripto = $scripto;
226 | $this->view->doc = $doc;
227 | $this->view->info = $info;
228 | $this->view->history = $history;
229 | $this->view->namespaceIndex = $this->_getParam('namespace-index');
230 | }
231 |
232 | /**
233 | * View a page revision.
234 | */
235 | public function revisionAction()
236 | {
237 | try {
238 | $scripto = ScriptoPlugin::getScripto();
239 | $doc = $scripto->getDocument($this->_getParam('item-id'));
240 | $doc->setPage($this->_getParam('file-id'));
241 | $revision = $scripto->getRevision($this->_getParam('revision-id'));
242 |
243 | // Handle a revert.
244 | if ($this->_getParam('scripto-page-revert')) {
245 | if (1 == $this->_getParam('namespace-index')) {
246 | $doc->editTalkPage($revision['wikitext']);
247 | } else {
248 | $doc->editTranscriptionPage($revision['wikitext']);
249 | }
250 | $this->_helper->flashMessenger(__('Successfully reverted the page to a previous revision.'), 'success');
251 | $this->_helper->redirector->gotoRoute(array('item-id' => $doc->getId(),
252 | 'file-id' => $doc->getPageId(),
253 | 'namespace-index' => $this->_getParam('namespace-index')),
254 | 'scripto_history');
255 | }
256 |
257 | } catch (Scripto_Exception $e) {
258 | $this->_helper->flashMessenger($e->getMessage());
259 | }
260 |
261 | $this->view->scripto = $scripto;
262 | $this->view->doc = $doc;
263 | $this->view->revision = $revision;
264 | $this->view->namespaceIndex = $this->_getParam('namespace-index');
265 | }
266 |
267 | /**
268 | * View diff between page revisions.
269 | */
270 | public function diffAction()
271 | {
272 | try {
273 | $scripto = ScriptoPlugin::getScripto();
274 | $doc = $scripto->getDocument($this->_getParam('item-id'));
275 | $doc->setPage($this->_getParam('file-id'));
276 | $diff = $scripto->getRevisionDiff($this->_getParam('old-revision-id'), $this->_getParam('revision-id'));
277 | $oldRevision = $scripto->getRevision($this->_getParam('old-revision-id'));
278 | $revision = $scripto->getRevision($this->_getParam('revision-id'));
279 | } catch (Scripto_Exception $e) {
280 | $this->_helper->flashMessenger($e->getMessage());
281 | $this->_helper->redirector->goto('index');
282 | }
283 |
284 | $this->view->scripto = $scripto;
285 | $this->view->doc = $doc;
286 | $this->view->diff = $diff;
287 | $this->view->namespaceIndex = $this->_getParam('namespace-index');
288 | $this->view->oldRevision = $oldRevision;
289 | $this->view->revision = $revision;
290 | }
291 |
292 | /**
293 | * Handle AJAX requests from the transcribe action.
294 | *
295 | * 403 Forbidden
296 | * 400 Bad Request
297 | * 500 Internal Server Error
298 | */
299 | public function pageActionAction()
300 | {
301 | // Don't render the view script.
302 | $this->_helper->viewRenderer->setNoRender(true);
303 |
304 | // Only allow AJAX requests.
305 | if (!$this->getRequest()->isXmlHttpRequest()) {
306 | $this->getResponse()->setHttpResponseCode(403);
307 | return;
308 | }
309 |
310 | // Allow only valid pages.
311 | $pages = array('transcription', 'talk');
312 | if (!in_array($this->_getParam('page'), $pages)) {
313 | $this->getResponse()->setHttpResponseCode(400);
314 | return;
315 | }
316 |
317 | // Only allow valid page actions.
318 | $pageActions = array('edit', 'watch', 'unwatch', 'protect', 'unprotect',
319 | 'import-page', 'import-document');
320 | if (!in_array($this->_getParam('page_action'), $pageActions)) {
321 | $this->getResponse()->setHttpResponseCode(400);
322 | return;
323 | }
324 |
325 | // Handle the page action.
326 | try {
327 | $scripto = ScriptoPlugin::getScripto();
328 | $doc = $scripto->getDocument($this->_getParam('item_id'));
329 | $doc->setPage($this->_getParam('file_id'));
330 |
331 | $body = null;
332 | switch ($this->_getParam('page_action')) {
333 | case 'edit':
334 | if ('talk' == $this->_getParam('page')) {
335 | $doc->editTalkPage($this->_getParam('wikitext'));
336 | $body = $doc->getTalkPageHtml();
337 | } else {
338 | $doc->editTranscriptionPage($this->_getParam('wikitext'));
339 | $body = $doc->getTranscriptionPageHtml();
340 | }
341 | break;
342 | case 'watch':
343 | $doc->watchPage();
344 | break;
345 | case 'unwatch':
346 | $doc->unwatchPage();
347 | break;
348 | case 'protect':
349 | if ('talk' == $this->_getParam('page')) {
350 | $doc->protectTalkPage();
351 | } else {
352 | $doc->protectTranscriptionPage();
353 | }
354 | break;
355 | case 'unprotect':
356 | if ('talk' == $this->_getParam('page')) {
357 | $doc->unprotectTalkPage();
358 | } else {
359 | $doc->unprotectTranscriptionPage();
360 | }
361 | break;
362 | case 'import-page':
363 | $doc->exportPage(get_option('scripto_import_type'));
364 | break;
365 | case 'import-document':
366 | $doc->export(get_option('scripto_import_type'));
367 | break;
368 | default:
369 | $this->getResponse()->setHttpResponseCode(400);
370 | return;
371 | }
372 |
373 | $this->getResponse()->setBody($body);
374 | } catch (Scripto_Exception $e) {
375 | $this->getResponse()
376 | ->setHttpResponseCode(500)
377 | ->setBody($e->getMessage());
378 | }
379 | }
380 | }
381 |
--------------------------------------------------------------------------------
/languages/template.base.pot:
--------------------------------------------------------------------------------
1 | # Translation for the Scripto plugin for Omeka.
2 | # Copyright (C) 2012 Roy Rosenzweig Center for History and New Media
3 | # This file is distributed under the same license as the Omeka package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: Scripto\n"
10 | "Report-Msgid-Bugs-To: http://github.com/omeka/plugin-Scripto/issues\n"
11 | "POT-Creation-Date: 2012-11-30 21:49-0500\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | msgid "un/protected"
21 | msgstr ""
22 |
23 | msgid "protected"
24 | msgstr ""
25 |
26 | msgid "unprotected"
27 | msgstr ""
28 |
29 | msgid "replaced"
30 | msgstr ""
31 |
32 | msgid "created"
33 | msgstr ""
34 |
35 | msgid "edited"
36 | msgstr ""
37 |
38 | msgid "protected by"
39 | msgstr ""
40 |
41 | msgid "unprotected by"
42 | msgstr ""
43 |
44 | msgid "replaced by"
45 | msgstr ""
46 |
47 | msgid "created by"
48 | msgstr ""
49 |
50 | msgid "edited by"
51 | msgstr ""
52 |
--------------------------------------------------------------------------------
/libraries/Scripto.php:
--------------------------------------------------------------------------------
1 |
58 | * $mediawiki['api_url']: required; the MediaWiki API URL
59 | * $mediawiki['pass_cookies']: optional pass cookies to the web
60 | * $mediawiki['cookie_prefix']: optional; set the cookie prefix
61 | * browser via API client
62 | *
63 | */
64 | public function __construct(Scripto_Adapter_Interface $adapter, $mediawiki)
65 | {
66 | // Set the adapter.
67 | $this->_adapter = $adapter;
68 |
69 | // Set the MediaWiki service.
70 | if ($mediawiki instanceof Scripto_Service_MediaWiki) {
71 | $this->_mediawiki = $mediawiki;
72 | } else if (is_array($mediawiki) && array_key_exists('api_url', $mediawiki)) {
73 | if (!isset($mediawiki['pass_cookies'])) {
74 | $mediawiki['pass_cookies'] = true;
75 | }
76 | if (!isset($mediawiki['cookie_prefix'])) {
77 | $mediawiki['cookie_prefix'] = null;
78 | }
79 |
80 | $this->_mediawiki = new Scripto_Service_MediaWiki($mediawiki['api_url'],
81 | (bool) $mediawiki['pass_cookies'],
82 | $mediawiki['cookie_prefix']);
83 | } else {
84 | throw new Scripto_Exception('The provided mediawiki parameter is invalid.');
85 | }
86 |
87 | // Set the user information.
88 | $this->setUserInfo();
89 | }
90 |
91 | /**
92 | * Provide a transparent interface for calling custom adapter methods.
93 | *
94 | * This makes it possible to call custom adapter methods (those not required
95 | * by Scripto_Adapter_Interface) directly from the Scripto object.
96 | *
97 | * @see Scripto_Adapter_Interface
98 | * @param string $name
99 | * @param array $args
100 | * @return mixed
101 | */
102 | public function __call($name, $args)
103 | {
104 | if (!method_exists($this->_adapter, $name)) {
105 | require_once 'Scripto/Adapter/Exception.php';
106 | throw new Scripto_Adapter_Exception('The provided adapter method "' . $name . '" does not exist.');
107 | }
108 | return call_user_func_array(array($this->_adapter, $name), $args);
109 | }
110 |
111 | /**
112 | * Check whether the specified document exists in the external system.
113 | *
114 | * @uses Scripto_Adapter_Interface::documentExists()
115 | * @param string|int $id The unique document identifier.
116 | * @return bool
117 | */
118 | public function documentExists($id)
119 | {
120 | // Query the adapter whether the document exists.
121 | if ($this->_adapter->documentExists($id)) {
122 | return true;
123 | }
124 | return false;
125 | }
126 |
127 | /**
128 | * Get a Scripto_Document object.
129 | *
130 | * @see Scripto_Document
131 | * @param string|int $id The unique document identifier.
132 | * @return Scripto_Document
133 | */
134 | public function getDocument($id)
135 | {
136 | return new Scripto_Document($id, $this->_adapter, $this->_mediawiki);
137 | }
138 |
139 | /**
140 | * Login via the MediaWiki service.
141 | *
142 | * It is possible to restrict account creation in MediaWiki.
143 | * @link http://www.mediawiki.org/wiki/Manual:Preventing_access#Restrict_account_creation
144 | *
145 | * @uses Scripto_Service_MediaWiki::login()
146 | * @param string $username The MediaWiki user's username.
147 | * @param string $password The MediaWiki user's password.
148 | */
149 | public function login($username, $password)
150 | {
151 | $this->_mediawiki->login($username, $password);
152 | $this->setUserInfo();
153 | }
154 |
155 | /**
156 | * Logout via the MediaWiki service.
157 | *
158 | * @uses Scripto_Service_MediaWiki::logout()
159 | */
160 | public function logout()
161 | {
162 | $this->_mediawiki->logout();
163 | $this->setUserInfo();
164 | }
165 |
166 | /**
167 | * Determine if the current user is logged in.
168 | *
169 | * @return bool
170 | */
171 | public function isLoggedIn()
172 | {
173 | // Check against the user ID. An anonymous user has an ID of 0.
174 | return (bool) $this->_userInfo['query']['userinfo']['id'];
175 | }
176 |
177 | /**
178 | * Determine if the current user can export transcriptions to the external
179 | * system.
180 | *
181 | * @param array $groups The MediaWiki groups allowed to export.
182 | * @return bool
183 | */
184 | public function canExport(array $groups = array('sysop', 'bureaucrat'))
185 | {
186 | foreach ($groups as $group) {
187 | if (in_array($group, $this->_userInfo['query']['userinfo']['groups'])) {
188 | return true;
189 | }
190 | }
191 | return false;
192 | }
193 |
194 | /**
195 | * Determine if the current user can protect MediaWiki pages.
196 | *
197 | * @return bool
198 | */
199 | public function canProtect()
200 | {
201 | // Users with protect rights can protect pages.
202 | if (in_array('protect', $this->_userInfo['query']['userinfo']['rights'])) {
203 | return true;
204 | }
205 | return false;
206 | }
207 |
208 | /**
209 | * Set the current user's information.
210 | *
211 | * Under normal circumstances calling this method directly is unnecessary,
212 | * but is helpful when authenticating after construction and when a login is
213 | * not called, like when hijacking cookies for command line authentication.
214 | *
215 | * @uses Scripto_Service_MediaWiki::getUserInfo()
216 | */
217 | public function setUserInfo()
218 | {
219 | $this->_userInfo = $this->_mediawiki->getUserInfo('groups|rights');
220 | }
221 |
222 | /**
223 | * Return the name of the current user.
224 | *
225 | * @return string
226 | */
227 | public function getUserName()
228 | {
229 | return $this->_userInfo['query']['userinfo']['name'];
230 | }
231 |
232 | /**
233 | * Get the current user's most recently contributed document pages.
234 | *
235 | * @uses Scripto_Service_MediaWiki::getUserContributions()
236 | * @param int $limit The number of document pages to return.
237 | * @return array
238 | */
239 | public function getUserDocumentPages($limit = 10)
240 | {
241 | $limit = (int) $limit;
242 | $userDocumentPages = array();
243 | $documentTitles = array();
244 | $start = null;
245 |
246 | // Namespaces to get: ns_index => ns_name
247 | // See http://www.mediawiki.org/wiki/Manual:Namespace#Built-in_namespaces
248 | $namespaces = array('0' => 'Main', '1' => 'Talk');
249 |
250 | do {
251 | $response = $this->_mediawiki->getUserContributions(
252 | $this->_userInfo['query']['userinfo']['name'],
253 | array('ucstart' => $start,
254 | 'ucnamespace' => implode('|', array_keys($namespaces)),
255 | 'uclimit' => 100)
256 | );
257 | foreach ($response['query']['usercontribs'] as $value) {
258 |
259 | // Filter out duplicate pages.
260 | if (array_key_exists($value['pageid'], $userDocumentPages)) {
261 | continue;
262 | }
263 |
264 | // Extract the title, removing the namespace if any.
265 | $title = preg_replace('/^(.+:)?(.+)$/', '$2', $value['title']);
266 |
267 | // Preempt further processing on contributions with an invalid
268 | // prefix.
269 | if (Scripto_Document::BASE_TITLE_PREFIX != $title[0]) {
270 | continue;
271 | }
272 |
273 | // Set the document ID and page ID.
274 | $documentIds = Scripto_Document::decodeBaseTitle($title);
275 |
276 | // Filter out contributions that are not valid document pages.
277 | if (!$this->_adapter->documentPageExists($documentIds[0], $documentIds[1])) {
278 | continue;
279 | }
280 |
281 | // Set the document title and document page name. Reduce calls
282 | // to the adapter by caching each document title, and checking
283 | // if they exist.
284 | if (array_key_exists($documentIds[0], $documentTitles)) {
285 | $documentTitle = $documentTitles[$documentIds[0]];
286 | } else {
287 | $documentTitle = $this->_adapter->getDocumentTitle($documentIds[0]);
288 | $documentTitles[$documentIds[0]] = $documentTitle;
289 | }
290 |
291 | // Duplicate pages have already been filtered out, so there is
292 | // no need to cache document page names.
293 | $documentPageName = $this->_adapter->getDocumentPageName($documentIds[0], $documentIds[1]);
294 |
295 | // Build the user document pages, newest properties first.
296 | $userDocumentPages[$value['pageid']] = array(
297 | 'revision_id' => $value['revid'],
298 | 'namespace_index' => $value['ns'],
299 | 'namespace_name' => $namespaces[$value['ns']],
300 | 'mediawiki_title' => $value['title'],
301 | 'timestamp' => $value['timestamp'],
302 | 'comment' => $value['comment'],
303 | 'size' => $value['size'],
304 | 'document_id' => $documentIds[0],
305 | 'document_page_id' => $documentIds[1],
306 | 'document_title' => $documentTitle,
307 | 'document_page_name' => $documentPageName,
308 | );
309 |
310 | // Break out of the loops if limit has been reached.
311 | if ($limit == count($userDocumentPages)) {
312 | break 2;
313 | }
314 | }
315 |
316 | // Set the query continue, if any.
317 | if (isset($response['query-continue'])) {
318 | $start = $response['query-continue']['usercontribs']['ucstart'];
319 | } else {
320 | $start = null;
321 | }
322 |
323 | } while ($start);
324 |
325 | return $userDocumentPages;
326 | }
327 |
328 | /**
329 | * Get the recent changes.
330 | *
331 | * @link http://www.mediawiki.org/wiki/Manual:Namespace#Built-in_namespaces
332 | * @uses Scripto_Service_MediaWiki::getRecentChanges()
333 | * @param int $limit The number of recent changes to return.
334 | * @return array
335 | */
336 | public function getRecentChanges($limit = 10)
337 | {
338 | $start = null;
339 | $recentChanges = array();
340 | $documentTitles = array();
341 | $documentPageNames = array();
342 |
343 | // Namespaces to get: ns_index => ns_name
344 | // See http://www.mediawiki.org/wiki/Manual:Namespace#Built-in_namespaces
345 | $namespaces = array('0' => 'Main', '1' => 'Talk');
346 |
347 | do {
348 | $response = $this->_mediawiki->getRecentChanges(
349 | array('rcprop' => 'user|comment|timestamp|title|ids|sizes|loginfo|flags',
350 | 'rclimit' => '100',
351 | 'rcnamespace' => implode('|', array_keys($namespaces)),
352 | 'rcstart' => $start)
353 | );
354 |
355 | foreach ($response['query']['recentchanges'] as $value) {
356 |
357 | // Extract the title, removing the namespace if any.
358 | $title = preg_replace('/^(.+:)?(.+)$/', '$2', $value['title']);
359 |
360 | // Preempt further processing on contributions with an invalid
361 | // prefix.
362 | if (Scripto_Document::BASE_TITLE_PREFIX != $title[0]) {
363 | continue;
364 | }
365 |
366 | // Set the document ID and page ID.
367 | $documentIds = Scripto_Document::decodeBaseTitle($title);
368 |
369 | // Set the document title and document page name. Reduce calls
370 | // to the adapter by caching each document title and page name,
371 | // and checking if they exist.
372 | $cachedDocument = array_key_exists($documentIds[0], $documentTitles);
373 | $cachedDocumentPage = array_key_exists($documentIds[1], $documentPageNames);
374 |
375 | // The document title and page name have been cached.
376 | if ($cachedDocument && $cachedDocumentPage) {
377 | $documentTitle = $documentTitles[$documentIds[0]];
378 | $documentPageName = $documentPageNames[$documentIds[1]];
379 |
380 | // The document title has been cached, but not the page name.
381 | } else if ($cachedDocument && !$cachedDocumentPage) {
382 | // Filter out invalid document pages.
383 | if (!$this->_adapter->documentPageExists($documentIds[0], $documentIds[1])) {
384 | continue;
385 | }
386 | $documentTitle = $documentTitles[$documentIds[0]];
387 | $documentPageName = $this->_adapter->getDocumentPageName($documentIds[0], $documentIds[1]);
388 | $documentPageNames[$documentIds[1]] = $documentPageName;
389 |
390 | // The document title and page name have not been cached.
391 | } else {
392 | // Filter out invalid document pages.
393 | if (!$this->_adapter->documentPageExists($documentIds[0], $documentIds[1])) {
394 | continue;
395 | }
396 | $documentTitle = $this->_adapter->getDocumentTitle($documentIds[0]);
397 | $documentTitles[$documentIds[0]] = $documentTitle;
398 | $documentPageName = $this->_adapter->getDocumentPageName($documentIds[0], $documentIds[1]);
399 | $documentPageNames[$documentIds[1]] = $documentPageName;
400 | }
401 |
402 | $logAction = isset($value['logaction']) ? $value['logaction']: null;
403 | $action = self::getChangeAction(array('comment' => $value['comment'],
404 | 'log_action' => $logAction));
405 |
406 | $recentChanges[] = array(
407 | 'type' => $value['type'],
408 | 'namespace_index' => $value['ns'],
409 | 'namespace_name' => $namespaces[$value['ns']],
410 | 'mediawiki_title' => $value['title'],
411 | 'rcid' => $value['rcid'],
412 | 'page_id' => $value['pageid'],
413 | 'revision_id' => $value['revid'],
414 | 'old_revision_id' => $value['old_revid'],
415 | 'user' => $value['user'],
416 | 'old_length' => $value['oldlen'],
417 | 'new_length' => $value['newlen'],
418 | 'timestamp' => $value['timestamp'],
419 | 'comment' => $value['comment'],
420 | 'action' => $action,
421 | 'log_id' => isset($value['logid']) ? $value['logid']: null,
422 | 'log_type' => isset($value['logtype']) ? $value['logtype']: null,
423 | 'log_action' => $logAction,
424 | 'new' => isset($value['new']) ? true: false,
425 | 'minor' => isset($value['minor']) ? true: false,
426 | 'document_id' => $documentIds[0],
427 | 'document_page_id' => $documentIds[1],
428 | 'document_title' => $documentTitle,
429 | 'document_page_name' => $documentPageName,
430 | );
431 |
432 | // Break out of the loops if limit has been reached.
433 | if ($limit == count($recentChanges)) {
434 | break 2;
435 | }
436 | }
437 |
438 | // Set the query continue, if any.
439 | if (isset($response['query-continue'])) {
440 | $start = $response['query-continue']['recentchanges']['rcstart'];
441 | } else {
442 | $start = null;
443 | }
444 |
445 | } while ($start);
446 |
447 | return $recentChanges;
448 | }
449 |
450 | /**
451 | * Get the current user's watchlist.
452 | *
453 | * @link http://www.mediawiki.org/wiki/API:Watchlist
454 | * @uses Scripto_Service_MediaWiki::getWatchlist()
455 | * @param int $limit The number of recent changes to return.
456 | * @return array
457 | */
458 | public function getWatchlist($limit = 10)
459 | {
460 | $start = null;
461 | $watchlist = array();
462 | $documentTitles = array();
463 | $documentPageNames = array();
464 |
465 | // Namespaces to get: ns_index => ns_name
466 | // See http://www.mediawiki.org/wiki/Manual:Namespace#Built-in_namespaces
467 | $namespaces = array('0' => 'Main', '1' => 'Talk');
468 |
469 | do {
470 | $response = $this->_mediawiki->getWatchlist(
471 | array('wlprop' => 'user|comment|timestamp|title|ids|sizes|flags',
472 | 'wllimit' => '100',
473 | 'wlallrev' => true,
474 | 'wlnamespace' => implode('|', array_keys($namespaces)),
475 | 'wlstart' => $start)
476 | );
477 |
478 | foreach ($response['query']['watchlist'] as $value) {
479 |
480 | // Extract the title, removing the namespace if any.
481 | $title = preg_replace('/^(.+:)?(.+)$/', '$2', $value['title']);
482 |
483 | // Preempt further processing on contributions with an invalid
484 | // prefix.
485 | if (Scripto_Document::BASE_TITLE_PREFIX != $title[0]) {
486 | continue;
487 | }
488 |
489 | // Set the document ID and page ID.
490 | $documentIds = Scripto_Document::decodeBaseTitle($title);
491 |
492 | // Set the document title and document page name. Reduce calls
493 | // to the adapter by caching each document title and page name,
494 | // and checking if they exist.
495 | $cachedDocument = array_key_exists($documentIds[0], $documentTitles);
496 | $cachedDocumentPage = array_key_exists($documentIds[1], $documentPageNames);
497 |
498 | // The document title and page name have been cached.
499 | if ($cachedDocument && $cachedDocumentPage) {
500 | $documentTitle = $documentTitles[$documentIds[0]];
501 | $documentPageName = $documentPageNames[$documentIds[1]];
502 |
503 | // The document title has been cached, but not the page name.
504 | } else if ($cachedDocument && !$cachedDocumentPage) {
505 | // Filter out invalid document pages.
506 | if (!$this->_adapter->documentPageExists($documentIds[0], $documentIds[1])) {
507 | continue;
508 | }
509 | $documentTitle = $documentTitles[$documentIds[0]];
510 | $documentPageName = $this->_adapter->getDocumentPageName($documentIds[0], $documentIds[1]);
511 | $documentPageNames[$documentIds[1]] = $documentPageName;
512 |
513 | // The document title and page name have not been cached.
514 | } else {
515 | // Filter out invalid document pages.
516 | if (!$this->_adapter->documentPageExists($documentIds[0], $documentIds[1])) {
517 | continue;
518 | }
519 | $documentTitle = $this->_adapter->getDocumentTitle($documentIds[0]);
520 | $documentTitles[$documentIds[0]] = $documentTitle;
521 | $documentPageName = $this->_adapter->getDocumentPageName($documentIds[0], $documentIds[1]);
522 | $documentPageNames[$documentIds[1]] = $documentPageName;
523 | }
524 |
525 | $action = self::getChangeAction(array('comment' => $value['comment'],
526 | 'revision_id' => $value['revid']));
527 |
528 | $watchlist[] = array(
529 | 'namespace_index' => $value['ns'],
530 | 'namespace_name' => $namespaces[$value['ns']],
531 | 'mediawiki_title' => $value['title'],
532 | 'page_id' => $value['pageid'],
533 | 'revision_id' => $value['revid'],
534 | 'user' => $value['user'],
535 | 'old_length' => $value['oldlen'],
536 | 'new_length' => $value['newlen'],
537 | 'timestamp' => $value['timestamp'],
538 | 'comment' => $value['comment'],
539 | 'action' => $action,
540 | 'new' => isset($value['new']) ? true: false,
541 | 'minor' => isset($value['minor']) ? true: false,
542 | 'anonymous' => isset($value['anon']) ? true: false,
543 | 'document_id' => $documentIds[0],
544 | 'document_page_id' => $documentIds[1],
545 | 'document_title' => $documentTitle,
546 | 'document_page_name' => $documentPageName,
547 | );
548 |
549 | // Break out of the loops if limit has been reached.
550 | if ($limit == count($watchlist)) {
551 | break 2;
552 | }
553 | }
554 |
555 | // Set the query continue, if any.
556 | if (isset($response['query-continue'])) {
557 | $start = $response['query-continue']['watchlist']['wlstart'];
558 | } else {
559 | $start = null;
560 | }
561 |
562 | } while ($start);
563 |
564 | return $watchlist;
565 | }
566 |
567 | /**
568 | * Get all documents from MediaWiki that have at least one page with text.
569 | *
570 | * @uses Scripto_Service_MediaWiki::getAllPages()
571 | * @return array An array following this format:
572 | *
573 | * array(
574 | * {document ID} => array(
575 | * ['mediawiki_titles'] => array(
576 | * {page ID} => {mediawiki title},
577 | * {...}
578 | * ),
579 | * ['document_title'] => {document title}
580 | * ),
581 | * {...}
582 | * )
583 | *
584 | */
585 | public function getAllDocuments()
586 | {
587 | $from = null;
588 | $documentTitles = array();
589 | $allDocuments = array();
590 | do {
591 | $response = $this->_mediawiki->getAllPages(
592 | array('aplimit' => 500,
593 | 'apminsize' => 1,
594 | 'apprefix' => Scripto_Document::BASE_TITLE_PREFIX,
595 | 'apfrom' => $from)
596 | );
597 |
598 | foreach ($response['query']['allpages'] as $value) {
599 |
600 | // Set the document ID and page ID.
601 | $documentIds = Scripto_Document::decodeBaseTitle($value['title']);
602 |
603 | // Set the page and continue if the document was already set.
604 | if (array_key_exists($documentIds[0], $documentTitles)) {
605 | $allDocuments[$documentIds[0]]['mediawiki_titles'][$documentIds[1]] = $value['title'];
606 | continue;
607 |
608 | // Set the document. Before getting the title, filter out pages
609 | // that are not valid documents.
610 | } else {
611 | if (!$this->_adapter->documentExists($documentIds[0])) {
612 | continue;
613 | }
614 | $documentTitle = $this->_adapter->getDocumentTitle($documentIds[0]);
615 | $documentTitles[$documentIds[0]] = $documentTitle;
616 | }
617 |
618 | $allDocuments[$documentIds[0]] = array(
619 | 'mediawiki_titles' => array($documentIds[1] => $value['title']),
620 | 'document_title' => $documentTitle,
621 | );
622 | }
623 |
624 | // Set the query continue, if any.
625 | if (isset($response['query-continue'])) {
626 | $from = $response['query-continue']['allpages']['apfrom'];
627 | } else {
628 | $from = null;
629 | }
630 |
631 | } while ($from);
632 |
633 | return $allDocuments;
634 | }
635 |
636 | /**
637 | * Get the difference between two page revisions.
638 | *
639 | * @uses Scripto_Service_MediaWiki::getRevisionDiff()
640 | * @param int $fromRevisionId The revision ID from which to diff.
641 | * @param int|string $toRevisionId The revision to which to diff. Use the
642 | * revision ID, "prev", "next", or "cur".
643 | * @return string An HTML table without the wrapping tag containing
644 | * difference markup, pre-formatted by MediaWiki. It is the responsibility
645 | * of implementers to wrap the result with table tags.
646 | */
647 | public function getRevisionDiff($fromRevisionId, $toRevisionId = 'prev')
648 | {
649 | return $this->_mediawiki->getRevisionDiff($fromRevisionId, $toRevisionId);
650 | }
651 |
652 | /**
653 | * Get properties of the specified page revision.
654 | *
655 | * @uses Scripto_Service_MediaWiki::getRevisions()
656 | * @param int $revisionId The ID of the rpage evision.
657 | * @return array
658 | */
659 | public function getRevision($revisionId)
660 | {
661 | // Get the revision properties.
662 | $response = $this->_mediawiki->getRevisions(
663 | null,
664 | array('revids' => $revisionId,
665 | 'rvprop' => 'ids|flags|timestamp|user|comment|size|content')
666 | );
667 | $page = current($response['query']['pages']);
668 |
669 | // Parse the wikitext into HTML.
670 | $response = $this->_mediawiki->parse(
671 | array('text' => '__NOEDITSECTION__' . $page['revisions'][0]['*'])
672 | );
673 |
674 | $action = self::getChangeAction(array('comment' => $page['revisions'][0]['comment']));
675 |
676 | $revision = array('revision_id' => $page['revisions'][0]['revid'],
677 | 'parent_id' => $page['revisions'][0]['parentid'],
678 | 'user' => $page['revisions'][0]['user'],
679 | 'timestamp' => $page['revisions'][0]['timestamp'],
680 | 'comment' => $page['revisions'][0]['comment'],
681 | 'size' => $page['revisions'][0]['size'],
682 | 'action' => $action,
683 | 'wikitext' => $page['revisions'][0]['*'],
684 | 'html' => $response['parse']['text']['*']);
685 | return $revision;
686 | }
687 |
688 | /**
689 | * Infer a change action verb from hints containted in various responses.
690 | *
691 | * @param array $hints Keyed hints from which to infer an change action:
692 | *
693 | * comment
694 | * log_action
695 | * revision_id
696 | *
697 | * @return string
698 | */
699 | static public function getChangeAction(array $hints = array())
700 | {
701 | $action = '';
702 |
703 | // Recent changes returns log_action=protect|unprotect with no comment.
704 | if (array_key_exists('log_action', $hints)) {
705 | $logActions = array('protect' => 'protected', 'unprotect' => 'unprotected');
706 | if (array_key_exists($hints['log_action'], $logActions)) {
707 | return $logActions[$hints['log_action']];
708 | }
709 | }
710 |
711 | // Infer from comment and revision_id.
712 | if (array_key_exists('comment', $hints)) {
713 | $commentActions = array('Replaced', 'Unprotected', 'Protected', 'Created');
714 | $actionPattern = '/^(' . implode('|', $commentActions) . ').+$/s';
715 | if (preg_match($actionPattern, $hints['comment'])) {
716 | $action = preg_replace_callback($actionPattern, function ($matches) {
717 | return strtolower($matches[1]);
718 | }, $hints['comment']);
719 | } else {
720 | // Watchlist returns revision_id=0 when the action is protect
721 | // or unprotect.
722 | if (array_key_exists('revision_id', $hints) && 0 == $hints['revision_id']) {
723 | $action = 'un/protected';
724 | } else {
725 | $action = 'edited';
726 | }
727 | }
728 | }
729 |
730 | return $action;
731 | }
732 |
733 | /**
734 | * Determine whether the provided MediaWiki API URL is valid.
735 | *
736 | * @uses Scripto_Service_MediaWiki::isValidApiUrl()
737 | * @param string $apiUrl The MediaWiki API URL to validate.
738 | * @return bool
739 | */
740 | static public function isValidApiUrl($apiUrl)
741 | {
742 | return Scripto_Service_MediaWiki::isValidApiUrl($apiUrl);
743 | }
744 |
745 | /**
746 | * Remove all HTML attributes from the provided markup.
747 | *
748 | * This filter is useful after getting HTML from the MediaWiki API, which
749 | * often contains MediaWiki-specific attributes that may conflict with local
750 | * settings.
751 | *
752 | * @see http://www.php.net/manual/en/domdocument.loadhtml.php#95251
753 | * @param string $html
754 | * @param array $exceptions Do not remove these attributes.
755 | * @return string
756 | */
757 | static public function removeHtmlAttributes($html, array $exceptions = array('href'))
758 | {
759 | // Check for an empty string.
760 | $html = trim($html ? $html : '');
761 | if (empty($html)) {
762 | return $html;
763 | }
764 |
765 | // Load the HTML into DOM. Must inject an XML declaration with encoding
766 | // set to UTF-8 to prevent DOMDocument from munging Unicode characters.
767 | $doc = new DOMDocument();
768 | $doc->loadHTML('' . $html);
769 | $xpath = new DOMXPath($doc);
770 |
771 | // Iterate over and remove attributes.
772 | foreach ($xpath->evaluate('//@*') as $attribute) {
773 | // Do not remove specified attributes.
774 | if (in_array($attribute->name, $exceptions)) {
775 | continue;
776 | }
777 | $attribute->ownerElement->removeAttributeNode($attribute);
778 | }
779 |
780 | return $doc->saveHTML();
781 | }
782 |
783 | /**
784 | * Remove all preprocessor limit reports from the provided markup.
785 | *
786 | * This filter is useful after getting HTML from the MediaWiki API, which
787 | * always contains a preprocessor limit report within hidden tags.
788 | *
789 | * @see http://en.wikipedia.org/wiki/Wikipedia:Template_limits#How_can_you_find_out.3F
790 | * @param string $text
791 | * @return string
792 | */
793 | static public function removeNewPPLimitReports($html)
794 | {
795 | // The "s" modifier means the "." meta-character will include newlines.
796 | // The "?" means the "+" quantifier is not greedy, thus will not remove
797 | // text between pages when importing document transcriptions.
798 | $html = preg_replace("//s", '', $html);
799 | return $html;
800 | }
801 | }
802 |
--------------------------------------------------------------------------------
/libraries/Scripto/Adapter/Example.php:
--------------------------------------------------------------------------------
1 | array(
31 | * 'document_title' => {documentTitle},
32 | * 'document_pages' => array(
33 | * {pageId} => array(
34 | * 'page_name' => {pageName},
35 | * 'page_file_url' => {pageFileUrl}
36 | * )
37 | * )
38 | * )
39 | *
40 | * Other adapters will likely get relevant data using the CMS API, and not
41 | * hardcode them like this example. Be sure to URL encode the document and
42 | * page IDs when transporting over HTTP. For example:
43 | *
44 | * documentId: Request for Purchase of Liver Oil & Drum Heads
45 | * pageId: xbe/XBE02001.jpg
46 | * ?documentId=Request+for+Purchase+of+Liver+Oil+%26+Drum+Heads&pageId=xbe%2FXBE02001.jpg
47 | *
48 | * These example documents are from Center for History and New Media Papers
49 | * of the War Department and Library of Congress American Memory.
50 | *
51 | * @var array
52 | */
53 | private $_documents = array(
54 | // Example of the preferred way to set the document and page IDs using
55 | // unique keys. See: http://wardepartmentpapers.org/document.php?id=16344
56 | 16344 => array(
57 | 'document_title' => 'Return of articles received and expended; work done at Springfield Massachusetts armory',
58 | 'document_pages' => array(
59 | 67799 => array(
60 | 'page_name' => 'Letter Outside',
61 | 'page_file_url' => 'http://wardepartmentpapers.org/images/medium/zto/ZTO07001.jpg'
62 | ),
63 | 67800 => array(
64 | 'page_name' => 'Letter Body',
65 | 'page_file_url' => 'http://wardepartmentpapers.org/images/medium/zto/ZTO07002.jpg'
66 | ),
67 | 67801 => array(
68 | 'page_name' => 'Worksheet 1, Outside',
69 | 'page_file_url' => 'http://wardepartmentpapers.org/images/medium/zto/ZTO07003.jpg'
70 | ),
71 | 67802 => array(
72 | 'page_name' => 'Worksheet 1, Page 1',
73 | 'page_file_url' => 'http://wardepartmentpapers.org/images/medium/zto/ZTO07004.jpg'
74 | ),
75 | 67803 => array(
76 | 'page_name' => 'Worksheet 1, Page 2',
77 | 'page_file_url' => 'http://wardepartmentpapers.org/images/medium/zto/ZTO07005.jpg'
78 | ),
79 | 67804 => array(
80 | 'page_name' => 'Worksheet 2, Outside',
81 | 'page_file_url' => 'http://wardepartmentpapers.org/images/medium/zto/ZTO07006.jpg'
82 | ),
83 | 67805 => array(
84 | 'page_name' => 'Worksheet 2, Page 1',
85 | 'page_file_url' => 'http://wardepartmentpapers.org/images/medium/zto/ZTO07007.jpg'
86 | )
87 | )
88 | ),
89 | // An alternate way to set the document using a document title as the
90 | // document ID and the file path as the page ID. See: http://books.google.com/books?id=eAuOQMmGEYIC&lpg=PA515&ots=PtWRBKDZbf&pg=PA515
91 | // %5BFacsimile%20of%5D%20letter%20to%20Messrs.%20O.%20P.%20Hall%20et%20al%20from%20Lincoln.
92 | '[Facsimile of] letter to Messrs. O. P. Hall et al from Lincoln.' => array(
93 | 'document_title' => '[Facsimile of] letter to Messrs. O. P. Hall et al from Lincoln.',
94 | 'document_pages' => array(
95 | // rbc%2Flprbscsm%2Fscsm0455%2F001r.jpg
96 | 'rbc/lprbscsm/scsm0455/001r.jpg' => array(
97 | 'page_name' => '001r',
98 | 'page_file_url' => 'http://memory.loc.gov/service/rbc/lprbscsm/scsm0455/001r.jpg'
99 | ),
100 | 'rbc/lprbscsm/scsm0455/002r.jpg' => array(
101 | 'page_name' => '002r',
102 | 'page_file_url' => 'http://memory.loc.gov/service/rbc/lprbscsm/scsm0455/002r.jpg'
103 | ),
104 | 'rbc/lprbscsm/scsm0455/003r.jpg' => array(
105 | 'page_name' => '003r',
106 | 'page_file_url' => 'http://memory.loc.gov/service/rbc/lprbscsm/scsm0455/003r.jpg'
107 | ),
108 | 'rbc/lprbscsm/scsm0455/004r.jpg' => array(
109 | 'page_name' => '004r',
110 | 'page_file_url' => 'http://memory.loc.gov/service/rbc/lprbscsm/scsm0455/004r.jpg'
111 | )
112 | )
113 | )
114 | );
115 |
116 | public function documentExists($documentId)
117 | {
118 | return array_key_exists($documentId, $this->_documents);
119 | }
120 |
121 | public function documentPageExists($documentId, $pageId)
122 | {
123 | if (!array_key_exists($documentId, $this->_documents)) {
124 | return false;
125 | }
126 | return array_key_exists($pageId, $this->_documents[$documentId]['document_pages']);
127 | }
128 |
129 | public function getDocumentPages($documentId)
130 | {
131 | if (!array_key_exists($documentId, $this->_documents)) {
132 | throw new Scripto_Adapter_Exception('Document does not exist.');
133 | }
134 | $pages = array();
135 | foreach ($this->_documents[$documentId]['document_pages'] as $pageId => $page) {
136 | $pages[$pageId] = $page['page_name'];
137 | }
138 | return $pages;
139 | }
140 |
141 | public function getDocumentPageFileUrl($documentId, $pageId)
142 | {
143 | if (!array_key_exists($documentId, $this->_documents)) {
144 | throw new Scripto_Adapter_Exception('Document does not exist.');
145 | }
146 | if (!array_key_exists($pageId, $this->_documents[$documentId]['document_pages'])) {
147 | throw new Scripto_Adapter_Exception('Document page does not exist.');
148 | }
149 | return $this->_documents[$documentId]['document_pages'][$pageId]['page_file_url'];
150 | }
151 |
152 | public function getDocumentFirstPageId($documentId)
153 | {
154 | if (!array_key_exists($documentId, $this->_documents)) {
155 | throw new Scripto_Adapter_Exception('Document does not exist.');
156 | }
157 | reset($this->_documents[$documentId]['document_pages']);
158 | return key($this->_documents[$documentId]['document_pages']);
159 | }
160 |
161 | public function getDocumentTitle($documentId)
162 | {
163 | if (!array_key_exists($documentId, $this->_documents)) {
164 | throw new Scripto_Adapter_Exception('Document does not exist.');
165 | }
166 | return $this->_documents[$documentId]['document_title'];
167 | }
168 |
169 | public function getDocumentPageName($documentId, $pageId)
170 | {
171 | if (!array_key_exists($documentId, $this->_documents)) {
172 | throw new Scripto_Adapter_Exception('Document does not exist.');
173 | }
174 | if (!array_key_exists($pageId, $this->_documents[$documentId]['document_pages'])) {
175 | throw new Scripto_Adapter_Exception('Document page does not exist.');
176 | }
177 | return $this->_documents[$documentId]['document_pages'][$pageId]['page_name'];
178 | }
179 |
180 | public function documentTranscriptionIsImported($documentId)
181 | {
182 | return false;
183 | }
184 |
185 | public function documentPageTranscriptionIsImported($documentId, $pageId)
186 | {
187 | return false;
188 | }
189 |
190 | public function importDocumentPageTranscription($documentId, $pageId, $text)
191 | {
192 | return false;
193 | }
194 |
195 | public function importDocumentTranscription($documentId, $text)
196 | {
197 | return false;
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/libraries/Scripto/Adapter/Exception.php:
--------------------------------------------------------------------------------
1 | [pageName], [...])
54 | *
55 | * Example return values:
56 | * array(2011 => 'Title Page',
57 | * 1999 => 'Page 1',
58 | * 4345 => 'Page 2')
59 | *
60 | * array('page_1' => 1,
61 | * 'page_2' => 2,
62 | * 'page_3' => 3)
63 | *
64 | * @param int|string $documentId The unique document ID
65 | * @return array An array containing page identifiers as keys and page names
66 | * as values, in sequential page order.
67 | */
68 | public function getDocumentPages($documentId);
69 |
70 | /**
71 | * Get the URL of the specified document page file.
72 | *
73 | * @param int|string $documentId The unique document ID
74 | * @param int|string $pageId The unique page ID
75 | * @return string The page file URL
76 | */
77 | public function getDocumentPageFileUrl($documentId, $pageId);
78 |
79 | /**
80 | * Get the first page of the document.
81 | *
82 | * @param int|string $documentId The document ID
83 | * @return int|string
84 | */
85 | public function getDocumentFirstPageId($documentId);
86 |
87 | /**
88 | * Get the title of the document.
89 | *
90 | * @param int|string $documentId The document ID
91 | * @return string
92 | */
93 | public function getDocumentTitle($documentId);
94 |
95 | /**
96 | * Get the name of the document page.
97 | *
98 | * @param int|string $documentId The document ID
99 | * @param int|string $pageId The unique page ID
100 | * @return string
101 | */
102 | public function getDocumentPageName($documentId, $pageId);
103 |
104 | /**
105 | * Indicate whether the document transcription has been imported.
106 | *
107 | * @param int|string $documentId The document ID
108 | * @return bool True: has been imported; false: has not been imported
109 | */
110 | public function documentTranscriptionIsImported($documentId);
111 |
112 | /**
113 | * Indicate whether the document page transcription has been imported.
114 | *
115 | * @param int|string $documentId The document ID
116 | * @param int|string $pageId The page ID
117 | */
118 | public function documentPageTranscriptionIsImported($documentId, $pageId);
119 |
120 | /**
121 | * Import a document page's transcription into the external system.
122 | *
123 | * @param int|string $documentId The document ID
124 | * @param int|string $pageId The page ID
125 | * @param string $text The text to import
126 | * @return bool True: success; false: fail
127 | */
128 | public function importDocumentPageTranscription($documentId, $pageId, $text);
129 |
130 | /**
131 | * Import an entire document's transcription into the external system.
132 | *
133 | * @param int|string The document ID
134 | * @param string The text to import
135 | * @return bool True: success; false: fail
136 | */
137 | public function importDocumentTranscription($documentId, $text);
138 | }
139 |
--------------------------------------------------------------------------------
/libraries/Scripto/Document.php:
--------------------------------------------------------------------------------
1 | documentExists($id)) {
108 | throw new Scripto_Exception("The specified document does not exist: {$this->_id}");
109 | }
110 |
111 | $this->_id = $id;
112 | $this->_adapter = $adapter;
113 | $this->_mediawiki = $mediawiki;
114 | $this->_title = $this->_adapter->getDocumentTitle($id);
115 | }
116 |
117 | /**
118 | * Set the current document page.
119 | *
120 | * Sets the current page ID, the base title used by MediaWiki, and
121 | * information about the MediaWiki transcription and talk pages.
122 | *
123 | * @param string|null $pageId The unique page identifier.
124 | */
125 | public function setPage($pageId)
126 | {
127 | // Set to the first page if the provided page is NULL or FALSE.
128 | if (null === $pageId || false === $pageId) {
129 | $pageId = $this->getFirstPageId();
130 | }
131 |
132 | // Check if the page exists.
133 | if (!$this->_adapter->documentPageExists($this->_id, $pageId)) {
134 | throw new Scripto_Exception("The specified page does not exist: $pageId");
135 | }
136 |
137 | // Mint the page title used by MediaWiki.
138 | $baseTitle = self::encodeBaseTitle($this->_id, $pageId);
139 |
140 | // Check if the base title is under the maximum character length.
141 | if (self::TITLE_BYTE_LIMIT < strlen($baseTitle)) {
142 | throw new Scripto_Exception('The document ID and/or page ID are too long to set the provided page.');
143 | }
144 |
145 | // Set information about the transcription and talk pages.
146 | $this->_transcriptionPageInfo = $this->_getPageInfo($baseTitle);
147 | $this->_talkPageInfo = $this->_getPageInfo('Talk:' . $baseTitle);
148 |
149 | $this->_pageId = $pageId;
150 | $this->_pageName = $this->_adapter->getDocumentPageName($this->_id, $pageId);
151 | $this->_baseTitle = $baseTitle;
152 | }
153 |
154 | /**
155 | * Get this document's ID.
156 | *
157 | * @return string|int
158 | */
159 | public function getId()
160 | {
161 | return $this->_id;
162 | }
163 |
164 | /**
165 | * Get this document's title.
166 | */
167 | public function getTitle()
168 | {
169 | return $this->_title;
170 | }
171 |
172 | /**
173 | * Get this document page's name.
174 | */
175 | public function getPageName()
176 | {
177 | return $this->_pageName;
178 | }
179 |
180 | /**
181 | * Get this document's current page ID.
182 | *
183 | * @return string|int
184 | */
185 | public function getPageId()
186 | {
187 | return $this->_pageId;
188 | }
189 |
190 | /**
191 | * Get this document's current base title.
192 | *
193 | * @return string
194 | */
195 | public function getBaseTitle()
196 | {
197 | if (is_null($this->_pageId)) {
198 | throw new Scripto_Exception('The document page must be set before getting the base title.');
199 | }
200 | return $this->_baseTitle;
201 | }
202 |
203 | /**
204 | * Get information about the current MediaWiki transcription page.
205 | *
206 | * @return array
207 | */
208 | public function getTranscriptionPageInfo()
209 | {
210 | if (is_null($this->_pageId)) {
211 | throw new Scripto_Exception('The document page must be set before getting information about the transcription page.');
212 | }
213 | return $this->_transcriptionPageInfo;
214 | }
215 |
216 | /**
217 | * Get information about the current MediaWiki talk page.
218 | *
219 | * @return array
220 | */
221 | public function getTalkPageInfo()
222 | {
223 | if (is_null($this->_pageId)) {
224 | throw new Scripto_Exception('The document page must be set before getting information about the talk page.');
225 | }
226 | return $this->_talkPageInfo;
227 | }
228 |
229 | /**
230 | * Get all of this document's pages from the adapter.
231 | *
232 | * @uses Scripto_Adapter_Interface::getDocumentPages()
233 | * @return array
234 | */
235 | public function getPages()
236 | {
237 | return (array) $this->_adapter->getDocumentPages($this->_id);
238 | }
239 |
240 | /**
241 | * Get this document's first page ID from the adapter.
242 | *
243 | * @uses Scripto_Adapter_Interface::getDocumentFirstPageId()
244 | * @return array
245 | */
246 | public function getFirstPageId()
247 | {
248 | return $this->_adapter->getDocumentFirstPageId($this->_id);
249 | }
250 |
251 | /**
252 | * Get this document's current page file URL from the adapter.
253 | *
254 | * @uses Scripto_Adapter_Interface::getDocumentPageFileUrl()
255 | * @return string
256 | */
257 | public function getPageFileUrl()
258 | {
259 | if (is_null($this->_pageId)) {
260 | throw new Scripto_Exception('The document page must be set before getting the page file URL.');
261 | }
262 | return $this->_adapter->getDocumentPageFileUrl($this->_id, $this->_pageId);
263 | }
264 |
265 | /**
266 | * Get the MediaWiki URL for the current transcription page.
267 | *
268 | * @return string
269 | */
270 | public function getTranscriptionPageMediawikiUrl()
271 | {
272 | if (is_null($this->_pageId)) {
273 | throw new Scripto_Exception('The document page must be set before getting the transcription page MediaWiki URL.');
274 | }
275 | return $this->_getPageMediawikiUrl($this->_baseTitle);
276 | }
277 |
278 | /**
279 | * Get the MediaWiki URL for the current talk page.
280 | *
281 | * @return string
282 | */
283 | public function getTalkPageMediawikiUrl()
284 | {
285 | if (is_null($this->_pageId)) {
286 | throw new Scripto_Exception('The document page must be set before getting the talk page MediaWiki URL.');
287 | }
288 | return $this->_getPageMediawikiUrl('Talk:' . $this->_baseTitle);
289 | }
290 |
291 | /**
292 | * Get the MediaWiki transcription page wikitext for the current page.
293 | *
294 | * @uses Scripto_Service_MediaWiki::getLatestRevisionWikitext()
295 | * @return string The transcription wikitext.
296 | */
297 | public function getTranscriptionPageWikitext()
298 | {
299 | if (is_null($this->_pageId)) {
300 | throw new Scripto_Exception('The document page must be set before getting the transcription page wikitext.');
301 | }
302 | return $this->_mediawiki->getLatestRevisionWikitext($this->_baseTitle);
303 | }
304 |
305 | /**
306 | * Get the MediaWiki talk page wikitext for the current page.
307 | *
308 | * @uses Scripto_Service_MediaWiki::getLatestRevisionWikitext()
309 | * @return string The talk wikitext.
310 | */
311 | public function getTalkPageWikitext()
312 | {
313 | if (is_null($this->_pageId)) {
314 | throw new Scripto_Exception('The document page must be set before getting the talk page wikitext.');
315 | }
316 | return $this->_mediawiki->getLatestRevisionWikitext('Talk:' . $this->_baseTitle);
317 | }
318 |
319 | /**
320 | * Get the MediaWiki transcription page HTML for the current page.
321 | *
322 | * @uses Scripto_Service_MediaWiki::getLatestRevisionHtml()
323 | * @return string The transcription HTML.
324 | */
325 | public function getTranscriptionPageHtml()
326 | {
327 | if (is_null($this->_pageId)) {
328 | throw new Scripto_Exception('The document page must be set before getting the transcription page HTML.');
329 | }
330 | return $this->_mediawiki->getLatestRevisionHtml($this->_baseTitle);
331 | }
332 |
333 | /**
334 | * Get the MediaWiki talk page HTML for the current page.
335 | *
336 | * @uses Scripto_Service_MediaWiki::getLatestRevisionHtml()
337 | * @return string The talk HTML.
338 | */
339 | public function getTalkPageHtml()
340 | {
341 | if (is_null($this->_pageId)) {
342 | throw new Scripto_Exception('The document page must be set before getting the talk page HTML.');
343 | }
344 | return $this->_mediawiki->getLatestRevisionHtml('Talk:' . $this->_baseTitle);
345 | }
346 |
347 | /**
348 | * Get the MediaWiki transcription page plain text for the current page.
349 | *
350 | * @uses Scripto_Service_MediaWiki::getLatestRevisionHtml()
351 | * @return string The transcription page plain text.
352 | */
353 | public function getTranscriptionPagePlainText()
354 | {
355 | if (is_null($this->_pageId)) {
356 | throw new Scripto_Exception('The document page must be set before getting the transcription page plain text.');
357 | }
358 | return html_entity_decode(strip_tags($this->_mediawiki->getLatestRevisionHtml($this->_baseTitle)));
359 | }
360 |
361 | /**
362 | * Get the MediaWiki talk plain text for the current page.
363 | *
364 | * @uses Scripto_Service_MediaWiki::getLatestRevisionHtml()
365 | * @return string The talk plain text.
366 | */
367 | public function getTalkPagePlainText()
368 | {
369 | if (is_null($this->_pageId)) {
370 | throw new Scripto_Exception('The document page must be set before getting the talk page plain text.');
371 | }
372 | return html_entity_decode(strip_tags($this->_mediawiki->getLatestRevisionHtml('Talk:' . $this->_baseTitle)));
373 | }
374 |
375 | /**
376 | * Get the MediaWiki transcription page revision history for the current page.
377 | *
378 | * @param int $limit The number of revisions to return.
379 | * @param int $startRevisionId The revision ID from which to start.
380 | * @return array
381 | */
382 | public function getTranscriptionPageHistory($limit = 10, $startRevisionId = null)
383 | {
384 | if (is_null($this->_pageId)) {
385 | throw new Scripto_Exception('The document page must be set before getting the transcription page history.');
386 | }
387 | return $this->_getPageHistory($this->_baseTitle, $limit, $startRevisionId);
388 | }
389 |
390 | /**
391 | * Get the MediaWiki talk page revision history for the current page.
392 | *
393 | * @param int $limit The number of revisions to return.
394 | * @param int $startRevisionId The revision ID from which to start.
395 | * @return array
396 | */
397 | public function getTalkPageHistory($limit = 10, $startRevisionId = null)
398 | {
399 | if (is_null($this->_pageId)) {
400 | throw new Scripto_Exception('The document page must be set before getting the talk page history.');
401 | }
402 | return $this->_getPageHistory('Talk:' . $this->_baseTitle, $limit, $startRevisionId);
403 | }
404 |
405 | /**
406 | * Determine if the current user can edit the MediaWiki transcription page.
407 | *
408 | * @return bool
409 | */
410 | public function canEditTranscriptionPage()
411 | {
412 | if (is_null($this->_pageId)) {
413 | throw new Scripto_Exception('The document page must be set before determining whether the user can edit the transcription page.');
414 | }
415 | return $this->_canEdit($this->_transcriptionPageInfo['protections']);
416 | }
417 |
418 | /**
419 | * Determine if the current user can edit the MediaWiki talk page.
420 | *
421 | * @return bool
422 | */
423 | public function canEditTalkPage()
424 | {
425 | if (is_null($this->_pageId)) {
426 | throw new Scripto_Exception('The document page must be set before determining whether the user can edit the talk page.');
427 | }
428 | return $this->_canEdit($this->_talkPageInfo['protections']);
429 | }
430 |
431 | /**
432 | * Edit the MediaWiki transcription page for the current document.
433 | *
434 | * @uses Scripto_Service_MediaWiki::edit()
435 | * @param string $text The wikitext of the transcription.
436 | */
437 | public function editTranscriptionPage($text)
438 | {
439 | if (is_null($this->_pageId)) {
440 | throw new Scripto_Exception('The document page must be set before editing the transcription page.');
441 | }
442 | $this->_mediawiki->edit($this->_baseTitle, $text);
443 | }
444 |
445 | /**
446 | * Edit the MediaWiki talk page for the current document.
447 | *
448 | * @uses Scripto_Service_MediaWiki::edit()
449 | * @param string $text The wikitext of the transcription.
450 | */
451 | public function editTalkPage($text)
452 | {
453 | if (is_null($this->_pageId)) {
454 | throw new Scripto_Exception('The document page must be set before editing the talk page.');
455 | }
456 | $this->_mediawiki->edit('Talk:' . $this->_baseTitle, $text);
457 | }
458 |
459 | /**
460 | * Protect the current transcription page.
461 | */
462 | public function protectTranscriptionPage()
463 | {
464 | if (is_null($this->_pageId)) {
465 | throw new Scripto_Exception('The document page must be set before protecting the transcription page.');
466 | }
467 | $this->_protectPage($this->_baseTitle, null);
468 |
469 | // Update information about this page.
470 | $this->_transcriptionPageInfo = $this->_getPageInfo($this->_baseTitle);
471 | }
472 |
473 | /**
474 | * Protect the current talk page.
475 | */
476 | public function protectTalkPage()
477 | {
478 | if (is_null($this->_pageId)) {
479 | throw new Scripto_Exception('The document page must be set before protecting the talk page.');
480 | }
481 | $this->_protectPage('Talk:' . $this->_baseTitle, null);
482 |
483 | // Update information about this page.
484 | $this->_talkPageInfo = $this->_getPageInfo('Talk:' . $this->_baseTitle);
485 | }
486 |
487 | /**
488 | * Unprotect the current transcription page.
489 | */
490 | public function unprotectTranscriptionPage()
491 | {
492 | if (is_null($this->_pageId)) {
493 | throw new Scripto_Exception('The document page must be set before unprotecting the transcription page.');
494 | }
495 | $this->_unprotectPage($this->_baseTitle, null);
496 |
497 | // Update information about this page.
498 | $this->_transcriptionPageInfo = $this->_getPageInfo($this->_baseTitle);
499 | }
500 |
501 | /**
502 | * Unprotect the current talk page.
503 | */
504 | public function unprotectTalkPage()
505 | {
506 | if (is_null($this->_pageId)) {
507 | throw new Scripto_Exception('The document page must be set before unprotecting the talk page.');
508 | }
509 | $this->_unprotectPage('Talk:' . $this->_baseTitle, null);
510 |
511 | // Update information about this page.
512 | $this->_talkPageInfo = $this->_getPageInfo('Talk:' . $this->_baseTitle);
513 | }
514 |
515 | /**
516 | * Watch the current page.
517 | *
518 | * Watching a transcription page implies watching its talk page.
519 | *
520 | * @uses Scripto_Service_MediaWiki::watch()
521 | */
522 | public function watchPage()
523 | {
524 | if (is_null($this->_pageId)) {
525 | throw new Scripto_Exception('The document page must be set before watching the page.');
526 | }
527 | $this->_mediawiki->watch($this->_baseTitle);
528 | }
529 |
530 | /**
531 | * Unwatch the current page.
532 | *
533 | * Unwatching a transcription page implies unwatching its talk page.
534 | *
535 | * @uses Scripto_Service_MediaWiki::watch()
536 | */
537 | public function unwatchPage()
538 | {
539 | if (is_null($this->_pageId)) {
540 | throw new Scripto_Exception('The document page must be set before unwatching the page.');
541 | }
542 | $this->_mediawiki->watch($this->_baseTitle, null, array('unwatch' => true));
543 | }
544 |
545 | /**
546 | * Determine whether the current transcription page is edit protected.
547 | *
548 | * @return bool
549 | */
550 | public function isProtectedTranscriptionPage()
551 | {
552 | if (is_null($this->_pageId)) {
553 | throw new Scripto_Exception('The document page must be set before determining whether the transcription page is protected.');
554 | }
555 | return $this->_isProtectedPage($this->_transcriptionPageInfo['protections']);
556 | }
557 |
558 | /**
559 | * Determine whether the current talk page is edit protected.
560 | *
561 | * @return bool
562 | */
563 | public function isProtectedTalkPage()
564 | {
565 | if (is_null($this->_pageId)) {
566 | throw new Scripto_Exception('The document page must be set before determining whether the talk page is protected.');
567 | }
568 | return $this->_isProtectedPage($this->_talkPageInfo['protections']);
569 | }
570 |
571 | /**
572 | * Determine whether the current user is watching the current page.
573 | *
574 | * @return bool
575 | */
576 | public function isWatchedPage()
577 | {
578 | if (is_null($this->_pageId)) {
579 | throw new Scripto_Exception('The document page must be set before determining whether the current user is watching the page.');
580 | }
581 | return $this->_transcriptionPageInfo['watched'];
582 | }
583 |
584 | /**
585 | * Determine whether all of this document's transcription pages were already
586 | * exported to the external system.
587 | *
588 | * @uses Scripto_Adapter_Interface::documentTranscriptionIsImported()
589 | * @return bool
590 | */
591 | public function isExported()
592 | {
593 | return $this->_adapter->documentTranscriptionIsImported($this->_id);
594 | }
595 |
596 | /**
597 | * Determine whether the current transcription page was already exported to
598 | * the external system.
599 | *
600 | * @uses Scripto_Adapter_Interface::documentPageTranscriptionIsImported()
601 | * @return bool
602 | */
603 | public function isExportedPage()
604 | {
605 | if (is_null($this->_pageId)) {
606 | throw new Scripto_Exception('The document page must be set before determining whether it is imported.');
607 | }
608 | return $this->_adapter->documentPageTranscriptionIsImported($this->_id, $this->_pageId);
609 | }
610 |
611 | /**
612 | * Export the document page transcription to the external system by calling
613 | * the adapter.
614 | *
615 | * @uses Scripto_Adapter_Interface::importDocumentPageTranscription()
616 | * @param string $type The type of text to set, valid options are
617 | * plain_text, html, and wikitext.
618 | */
619 | public function exportPage($type = 'plain_text')
620 | {
621 | switch ($type) {
622 | case 'plain_text':
623 | $text = $this->getTranscriptionPagePlainText();
624 | break;
625 | case 'html':
626 | $text = $this->getTranscriptionPageHtml();
627 | break;
628 | case 'wikitext':
629 | $text = $this->getTranscriptionPageWikitext();
630 | break;
631 | default:
632 | throw new Scripto_Exception('The provided import type is invalid.');
633 | }
634 | $this->_adapter->importDocumentPageTranscription($this->_id,
635 | $this->_pageId,
636 | trim($text));
637 | }
638 |
639 | /**
640 | * Export the entire document transcription to the external system by
641 | * calling the adapter.
642 | *
643 | * @uses Scripto_Adapter_Interface::importDocumentTranscription()
644 | * @param string $type The type of text to set, valid options are
645 | * plain_text, html, and wikitext.
646 | * @param string $pageDelimiter The delimiter used to stitch pages together.
647 | */
648 | public function export($type = 'plain_text', $pageDelimiter = "\n")
649 | {
650 | $text = array();
651 | foreach ($this->getPages() as $pageId => $pageName) {
652 | $baseTitle = self::encodeBaseTitle($this->_id, $pageId);
653 | switch ($type) {
654 | case 'plain_text':
655 | $text[] = html_entity_decode(strip_tags($this->_mediawiki->getLatestRevisionHtml($baseTitle)));
656 | break;
657 | case 'html':
658 | $text[] = $this->_mediawiki->getLatestRevisionHtml($baseTitle);
659 | break;
660 | case 'wikitext':
661 | $text[] = $this->_mediawiki->getLatestRevisionWikitext($baseTitle);
662 | break;
663 | default:
664 | throw new Scripto_Exception('The provided import type is invalid.');
665 | }
666 | }
667 | $text = implode($pageDelimiter, array_map('trim', $text));
668 | $this->_adapter->importDocumentTranscription($this->_id, trim($text));
669 | }
670 |
671 | /**
672 | * Determine if the current user can edit the specified MediaWiki page.
673 | *
674 | * @uses Scripto_Service_MediaWiki::getUserInfo()
675 | * @param array $pageProtections
676 | * @return bool
677 | */
678 | protected function _canEdit(array $pageProtections)
679 | {
680 | $userInfo = $this->_mediawiki->getUserInfo('rights');
681 |
682 | // Users without edit rights cannot edit pages.
683 | if (!in_array('edit', $userInfo['query']['userinfo']['rights'])) {
684 | return false;
685 | }
686 |
687 | // Users with edit rights can edit unprotected pages.
688 | if (empty($pageProtections)) {
689 | return true;
690 | }
691 |
692 | // Iterate the page protections.
693 | foreach ($pageProtections as $pageProtection) {
694 |
695 | // The page is edit-protected.
696 | if ('edit' == $pageProtection['type']) {
697 |
698 | // Users with edit and protect rights can edit protected pages.
699 | if (in_array('protect', $userInfo['query']['userinfo']['rights'])) {
700 | return true;
701 |
702 | // Users with edit but without protect rights cannot edit
703 | // protected pages.
704 | } else {
705 | return false;
706 | }
707 | }
708 | }
709 |
710 | // Users with edit rights can edit pages that are not edit-protected.
711 | return true;
712 | }
713 |
714 | /**
715 | * Determine whether the provided protections contain an edit protection.
716 | *
717 | * @param array $pageProtections The page protections from the page info:
718 | * {@link Scripto_Document::$_transcriptionPageInfo} or
719 | * {@link Scripto_Document::$_talkPageInfo}.
720 | * @return bool
721 | */
722 | protected function _isProtectedPage(array $pageProtections)
723 | {
724 | // There are no protections.
725 | if (empty($pageProtections)) {
726 | return false;
727 | }
728 |
729 | // Iterate the page protections.
730 | foreach ($pageProtections as $pageProtection) {
731 | // The page is edit protected.
732 | if ('edit' == $pageProtection['type'] || 'create' == $pageProtection['type']) {
733 | return true;
734 | }
735 | }
736 |
737 | // There are no edit protections.
738 | return false;
739 | }
740 |
741 | /**
742 | * Protect the specified page.
743 | *
744 | * @uses Scripto_Service_MediaWiki::protect()
745 | * @param string $title
746 | * @param string $protectToken
747 | */
748 | protected function _protectPage($title, $protectToken)
749 | {
750 | if ($this->_mediawiki->pageCreated($title)) {
751 | $protections = 'edit=sysop';
752 | } else {
753 | $protections = 'create=sysop';
754 | }
755 | $this->_mediawiki->protect($title, $protections, $protectToken);
756 | }
757 |
758 | /**
759 | * Unprotect the specified page.
760 | *
761 | * @uses Scripto_Service_MediaWiki::protect()
762 | * @param string $title
763 | * @param string $protectToken
764 | */
765 | protected function _unprotectPage($title, $protectToken)
766 | {
767 | if ($this->_mediawiki->pageCreated($title)) {
768 | $protections = 'edit=all';
769 | } else {
770 | $protections = 'create=all';
771 | }
772 | $this->_mediawiki->protect($title, $protections, $protectToken);
773 | }
774 |
775 | /**
776 | * Get the MediaWiki URL for the specified page.
777 | *
778 | * @uses Scripto_Service_MediaWiki::getSiteInfo()
779 | * @param string $title
780 | * @return string
781 | */
782 | protected function _getPageMediawikiUrl($title)
783 | {
784 | $siteInfo = $this->_mediawiki->getSiteInfo();
785 | return $siteInfo['query']['general']['server']
786 | . str_replace('$1', $title, $siteInfo['query']['general']['articlepath']);
787 | }
788 |
789 | /**
790 | * Get information for the specified page.
791 | *
792 | * @uses Scripto_Service_MediaWiki::getInfo()
793 | * @param string $title
794 | * @return array
795 | */
796 | protected function _getPageInfo($title)
797 | {
798 | $params = array('inprop' => 'protection|talkid|subjectid|url|watched');
799 | $response = $this->_mediawiki->getInfo($title, $params);
800 | $page = current($response['query']['pages']);
801 | $pageInfo = array('page_id' => isset($page['pageid']) ? $page['pageid'] : null,
802 | 'namespace_index' => isset($page['ns']) ? $page['ns'] : null,
803 | 'mediawiki_title' => isset($page['title']) ? $page['title'] : null,
804 | 'last_revision_id' => isset($page['lastrevid']) ? $page['lastrevid'] : null,
805 | 'counter' => isset($page['counter']) ? $page['counter'] : null,
806 | 'length' => isset($page['length']) ? $page['length'] : null,
807 | 'start_timestamp' => isset($page['starttimestamp']) ? $page['starttimestamp'] : null,
808 | 'protections' => isset($page['protection']) ? $page['protection'] : null,
809 | 'talk_id' => isset($page['talkid']) ? $page['talkid'] : null,
810 | 'mediawiki_full_url' => isset($page['fullurl']) ? $page['fullurl'] : null,
811 | 'mediawiki_edit_url' => isset($page['editurl']) ? $page['editurl'] : null,
812 | 'watched' => isset($page['watched']) ? true: false,
813 | 'redirect' => isset($page['redirect']) ? true: false,
814 | 'new' => isset($page['new']) ? true: false);
815 | return $pageInfo;
816 | }
817 |
818 | /**
819 | * Get the revisions for the specified page.
820 | *
821 | * @uses Scripto_Service_MediaWiki::getRevisions()
822 | * @param string $title
823 | * @param int $limit
824 | * @param int $startRevisionId
825 | * @return array
826 | */
827 | protected function _getPageHistory($title, $limit = 10, $startRevisionId = null)
828 | {
829 | $revisions = array();
830 | do {
831 | $response = $this->_mediawiki->getRevisions(
832 | $title,
833 | array('rvstartid' => $startRevisionId,
834 | 'rvlimit' => 100,
835 | 'rvprop' => 'ids|flags|timestamp|user|comment|size')
836 | );
837 | $page = current($response['query']['pages']);
838 |
839 | // Return if the page has not been created.
840 | if (array_key_exists('missing', $page)) {
841 | return $revisions;
842 | }
843 |
844 | foreach ($page['revisions'] as $revision) {
845 |
846 | $action = Scripto::getChangeAction(array('comment' => $revision['comment']));
847 |
848 | // Build the revisions.
849 | $revisions[] = array(
850 | 'revision_id' => $revision['revid'],
851 | 'parent_id' => $revision['parentid'],
852 | 'user' => $revision['user'],
853 | 'timestamp' => $revision['timestamp'],
854 | 'comment' => $revision['comment'],
855 | 'size' => $revision['size'],
856 | 'action' => $action,
857 | );
858 |
859 | // Break out of the loops if limit has been reached.
860 | if ($limit == count($revisions)) {
861 | break 2;
862 | }
863 | }
864 |
865 | // Set the query continue, if any.
866 | if (isset($response['query-continue'])) {
867 | $startRevisionId = $response['query-continue']['revisions']['rvstartid'];
868 | } else {
869 | $startRevisionId = null;
870 | }
871 |
872 | } while ($startRevisionId);
873 |
874 | return $revisions;
875 | }
876 |
877 | /**
878 | * Encode a base title that enables fail-safe document page transport
879 | * between the external system, Scripto, and MediaWiki.
880 | *
881 | * The base title is the base MediaWiki page title that corresponds to the
882 | * document page. Encoding is necessary to allow all Unicode characters in
883 | * document and page IDs, even those not allowed in URL syntax and MediaWiki
884 | * naming conventions. Encoding in Base64 allows the title to be decoded.
885 | *
886 | * The base title has four parts:
887 | *
888 | * A title prefix to keep MediaWiki from capitalizing the first
889 | * character
890 | * A URL-safe Base64 encoded document ID
891 | * A delimiter between the encoded document ID and page ID
892 | * A URL-safe Base64 encoded page ID
893 | *
894 | *
895 | * @link http://en.wikipedia.org/wiki/Base64#URL_applications
896 | * @link http://en.wikipedia.org/wiki/Wikipedia:Naming_conventions_%28technical_restrictions%29
897 | * @param string|int $documentId The document ID
898 | * @param string|int $pageId The page ID
899 | * @return string The encoded base title
900 | */
901 | static public function encodeBaseTitle($documentId, $pageId)
902 | {
903 | return self::BASE_TITLE_PREFIX
904 | . Scripto_Document::base64UrlEncode($documentId)
905 | . self::BASE_TITLE_DELIMITER
906 | . Scripto_Document::base64UrlEncode($pageId);
907 | }
908 |
909 | /**
910 | * Decode the base title.
911 | *
912 | * @param string|int $baseTitle
913 | * @return array An array containing the document ID and page ID
914 | */
915 | static public function decodeBaseTitle($baseTitle)
916 | {
917 | // First remove the title prefix.
918 | $baseTitle = ltrim($baseTitle, self::BASE_TITLE_PREFIX);
919 | // Create an array containing the document ID and page ID.
920 | $baseTitle = explode(self::BASE_TITLE_DELIMITER, $baseTitle);
921 | // URL-safe Base64 decode the array and return it.
922 | return array_map('Scripto_Document::base64UrlDecode', $baseTitle);
923 | }
924 |
925 | /**
926 | * Encode a string to URL-safe Base64.
927 | *
928 | * @link http://en.wikipedia.org/wiki/Base64#URL_applications
929 | * @param string $str
930 | * @return string
931 | */
932 | static public function base64UrlEncode($str)
933 | {
934 | return strtr(rtrim(base64_encode($str), '='), '+/', '-_');
935 | }
936 |
937 | /**
938 | * Decode a string from a URL-safe Base64.
939 | *
940 | * @param string $str
941 | * @return string
942 | */
943 | static public function base64UrlDecode($str)
944 | {
945 | return base64_decode(strtr($str, '-_', '+/'));
946 | }
947 | }
948 |
--------------------------------------------------------------------------------
/libraries/Scripto/Exception.php:
--------------------------------------------------------------------------------
1 | array(
55 | 'text', 'title', 'page', 'prop', 'pst', 'uselang'
56 | ),
57 | 'edit' => array(
58 | 'title', 'section', 'text', 'token', 'summary', 'minor', 'notminor',
59 | 'bot', 'basetimestamp', 'starttimestamp', 'recreate', 'createonly',
60 | 'nocreate', 'watchlist', 'md5', 'captchaid', 'captchaword', 'undo',
61 | 'undoafter'
62 | ),
63 | 'protect' => array(
64 | 'title', 'token', 'protections', 'expiry', 'reason', 'cascade'
65 | ),
66 | 'watch' => array(
67 | 'title', 'unwatch', 'token'
68 | ),
69 | 'query' => array(
70 | // title specifications
71 | 'titles', 'revids', 'pageids',
72 | // submodules
73 | 'meta', 'prop', 'list', 'type',
74 | // meta submodule
75 | 'siprop', 'sifilteriw', 'sishowalldb', 'sinumberingroup',
76 | 'uiprop',
77 | // prop submodule
78 | 'inprop', 'intoken', 'indexpageids', 'incontinue',
79 | 'rvprop', 'rvcontinue', 'rvlimit', 'rvstartid', 'rvendid',
80 | 'rvstart', 'rvend', 'rvdir', 'rvuser', 'rvexcludeuser',
81 | 'rvexpandtemplates', 'rvgeneratexml', 'rvsection', 'rvtoken',
82 | 'rvdiffto', 'rvdifftotext',
83 | // list submodule
84 | 'ucprop', 'ucuser', 'ucuserprefix', 'ucstart', 'ucend',
85 | 'uccontinue', 'ucdir', 'uclimit', 'ucnamespace', 'ucshow',
86 | 'rcprop', 'rcstart', 'rcend', 'rcdir', 'rclimit', 'rcnamespace',
87 | 'rcuser', 'rcexcludeuser', 'rctype', 'rcshow',
88 | 'wlprop', 'wlstart', 'wlend', 'wldir', 'wllimit', 'wlnamespace',
89 | 'wluser', 'wlexcludeuser', 'wlowner', 'wltoken', 'wlallrev',
90 | 'wlshow',
91 | 'aplimit', 'apminsize', 'apmaxsize', 'apprefix', 'apfrom',
92 | 'apnamespace', 'apfilterredir', 'apfilterlanglinks', 'apprtype',
93 | 'apprlevel', 'apdir',
94 | ),
95 | 'login' => array(
96 | 'lgname', 'lgpassword', 'lgtoken'
97 | ),
98 | 'logout' => array('token')
99 | );
100 |
101 | /**
102 | * Constructs the MediaWiki API client.
103 | *
104 | * @link http://www.mediawiki.org/wiki/API:Main_page
105 | * @param string $apiUrl The URL to the MediaWiki API.
106 | * @param bool $passCookies Pass cookies to the web browser.
107 | * @param string $cookiePrefix
108 | */
109 | public function __construct($apiUrl, $passCookies = true, $cookiePrefix = null)
110 | {
111 | $this->_passCookies = (bool) $passCookies;
112 |
113 | if (null !== $cookiePrefix) {
114 | $this->_cookiePrefix = $cookiePrefix;
115 | } elseif (isset($_COOKIE[self::COOKIE_NS . 'cookieprefix'])) {
116 | // Set the cookie prefix that was set by MediaWiki during login.
117 | $this->_cookiePrefix = $_COOKIE[self::COOKIE_NS . 'cookieprefix'];
118 | }
119 |
120 | // Set the HTTP client for the MediaWiki API .
121 | self::getHttpClient()->setUri($apiUrl)
122 | ->setConfig(array('keepalive' => true))
123 | ->setCookieJar();
124 |
125 | // Add X-Forwarded-For header if applicable.
126 | if (isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['SERVER_ADDR'])) {
127 | self::getHttpClient()->setHeaders('X-Forwarded-For',
128 | $_SERVER['REMOTE_ADDR'] . ', ' . $_SERVER['SERVER_ADDR']);
129 | }
130 |
131 | // If MediaWiki API authentication cookies are being passed and the
132 | // MediaWiki cookieprefix is set, get the cookies from the browser and
133 | // add them to the HTTP client cookie jar. Doing so maintains state
134 | // between browser requests.
135 | if ($this->_passCookies && $this->_cookiePrefix) {
136 | require_once 'Zend/Http/Cookie.php';
137 | foreach ($this->_cookieSuffixes as $cookieSuffix) {
138 | $cookieName = self::COOKIE_NS . $this->_cookiePrefix . $cookieSuffix;
139 | if (array_key_exists($cookieName, $_COOKIE)) {
140 | $cookie = new Zend_Http_Cookie($this->_cookiePrefix . $cookieSuffix,
141 | $_COOKIE[$cookieName],
142 | self::getHttpClient()->getUri()->getHost());
143 | self::getHttpClient()->getCookieJar()->addCookie($cookie);
144 | }
145 | }
146 | }
147 | }
148 |
149 | /**
150 | * Gets information about the current user.
151 | *
152 | * @link http://www.mediawiki.org/wiki/API:Meta#userinfo_.2F_ui
153 | * @param string $uiprop
154 | * @return array
155 | */
156 | public function getUserInfo($uiprop = '')
157 | {
158 | $params = array('meta' => 'userinfo',
159 | 'uiprop' => $uiprop);
160 | return $this->query($params);
161 | }
162 |
163 | /**
164 | * Gets overall site information.
165 | *
166 | * @link http://www.mediawiki.org/wiki/API:Meta#siteinfo_.2F_si
167 | * @param string $siprop
168 | * @return array
169 | */
170 | public function getSiteInfo($siprop = 'general')
171 | {
172 | $params = array('meta' => 'siteinfo',
173 | 'siprop' => $siprop);
174 | return $this->query($params);
175 | }
176 |
177 | /**
178 | * Gets a list of contributions made by a given user.
179 | *
180 | * @link http://www.mediawiki.org/wiki/API:Usercontribs
181 | * @param string $ucuser
182 | * @param array $params
183 | * @return array
184 | */
185 | public function getUserContributions($ucuser, array $params = array())
186 | {
187 | $params['ucuser'] = $ucuser;
188 | $params['list'] = 'usercontribs';
189 | return $this->query($params);
190 | }
191 |
192 | /**
193 | * Gets all recent changes to the wiki.
194 | *
195 | * @link http://www.mediawiki.org/wiki/API:Recentchanges
196 | * @param array $params
197 | * @return array
198 | */
199 | public function getRecentChanges(array $params = array())
200 | {
201 | $params['list'] = 'recentchanges';
202 | return $this->query($params);
203 | }
204 |
205 | /**
206 | * Gets a list of pages on the current user's watchlist.
207 | *
208 | * @link http://www.mediawiki.org/wiki/API:Watchlist
209 | * @param array $params
210 | * @return array
211 | */
212 | public function getWatchlist(array $params = array())
213 | {
214 | $params['list'] = 'watchlist';
215 | return $this->query($params);
216 | }
217 |
218 | /**
219 | * Gets a list of pages.
220 | *
221 | * @link http://www.mediawiki.org/wiki/API:Allpages
222 | * @param array $params
223 | * @return array
224 | */
225 | public function getAllPages(array $params = array())
226 | {
227 | $params['list'] = 'allpages';
228 | return $this->query($params);
229 | }
230 |
231 | /**
232 | * Gets basic page information.
233 | *
234 | * @link http://www.mediawiki.org/wiki/API:Properties#info_.2F_in
235 | * @param string $titles
236 | * @param array $params
237 | * @return array
238 | */
239 | public function getInfo($titles, array $params = array())
240 | {
241 | $params['titles'] = $titles;
242 | $params['prop'] = 'info';
243 | return $this->query($params);
244 | }
245 |
246 | /**
247 | * Gets revisions for a given page.
248 | *
249 | * @link http://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv
250 | * @param string $titles
251 | * @param array $params
252 | * @return array
253 | */
254 | public function getRevisions($titles, array $params = array())
255 | {
256 | $params['titles'] = $titles;
257 | $params['prop'] = 'revisions';
258 | return $this->query($params);
259 | }
260 |
261 | /**
262 | * Gets the HTML of a specified revision of a given page.
263 | *
264 | * @param int $revisionId
265 | * @return string
266 | */
267 | public function getRevisionHtml($revisionId)
268 | {
269 | // Get the revision wikitext.
270 | $response = $this->getRevisions(null, array('revids' => $revisionId,
271 | 'rvprop' => 'content'));
272 | $page = current($response['query']['pages']);
273 |
274 | // Parse the wikitext into HTML.
275 | $response = $this->parse(
276 | array('text' => '__NOEDITSECTION__' . $page['revisions'][0]['*'])
277 | );
278 | return $response['parse']['text']['*'];
279 | }
280 |
281 | /**
282 | * Gets the difference between two revisions.
283 | *
284 | * @param int $from The revision ID to diff.
285 | * @param int|string $to The revision to diff to: use the revision ID,
286 | * prev, next, or cur.
287 | * @return string The API returns preformatted table rows without a wrapping
288 | * . Presumably this is so implementers can wrap a custom .
289 | */
290 | public function getRevisionDiff($fromRevisionId, $toRevisionId = 'prev')
291 | {
292 | $response = $this->getRevisions(null, array('revids' => $fromRevisionId,
293 | 'rvdiffto' => $toRevisionId));
294 | $page = current($response['query']['pages']);
295 | return $page['revisions'][0]['diff']['*'];
296 | }
297 |
298 | /**
299 | * Gets the edit token for a given page.
300 | *
301 | * @link http://www.mediawiki.org/wiki/API:Edit#Token
302 | * @param string $title
303 | * @return string
304 | */
305 | public function getEditToken($title)
306 | {
307 | $response = $this->query(['meta' => 'tokens', 'type' => 'csrf']);
308 | return $response['query']['tokens']['csrftoken'];
309 | }
310 |
311 | /**
312 | * Gets the protect token for a given page.
313 | *
314 | * @link http://www.mediawiki.org/wiki/API:Protect#Token
315 | * @param string $title
316 | * @return string
317 | */
318 | public function getProtectToken($title)
319 | {
320 | $response = $this->query(['meta' => 'tokens', 'type' => 'csrf']);
321 | return $response['query']['tokens']['csrftoken'];
322 | }
323 |
324 | /**
325 | * Gets the watch token for a given page.
326 | *
327 | * @link http://www.mediawiki.org/wiki/API:Watch#Token
328 | * @param string $title
329 | * @return string
330 | */
331 | public function getWatchToken($title)
332 | {
333 | $response = $this->query(['meta' => 'tokens', 'type' => 'watch']);
334 | return $response['query']['tokens']['watchtoken'];
335 | }
336 |
337 | /**
338 | * Gets the protections for a given page.
339 | *
340 | * @link http://www.mediawiki.org/wiki/API:Properties#info_.2F_in
341 | * @param string $title
342 | * @return array
343 | */
344 | public function getPageProtections($title)
345 | {
346 | $response = $this->getInfo($title, array('inprop' => 'protection'));
347 | $page = current($response['query']['pages']);
348 | return $page['protection'];
349 | }
350 |
351 | /**
352 | * Gets the wikitext of the latest revision of a given page.
353 | *
354 | * @link http://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv
355 | * @param string $title
356 | * @return string|null
357 | */
358 | public function getLatestRevisionWikitext($title)
359 | {
360 | $response = $this->getRevisions($title, array('rvprop' => 'content',
361 | 'rvlimit' => '1'));
362 | $page = current($response['query']['pages']);
363 |
364 | // Return the wikitext only if the page already exists.
365 | $wikitext = null;
366 | if (isset($page['revisions'][0]['*'])) {
367 | $wikitext = $page['revisions'][0]['*'];
368 | }
369 | return $wikitext;
370 | }
371 |
372 | /**
373 | * Gets the HTML of the latest revision of a given page.
374 | *
375 | * @link http://www.mediawiki.org/wiki/API:Parsing_wikitext#parse
376 | * @param string $title
377 | * @return string|null
378 | */
379 | public function getLatestRevisionHtml($title)
380 | {
381 | // To exclude [edit] links in the parsed wikitext, we must use the
382 | // following hack.
383 | $response = $this->parse(array('text' => '__NOEDITSECTION__{{:' . $title . '}}'));
384 |
385 | // Return the text only if the page already exists. Otherwise, the
386 | // returned HTML is a link to the document's MediaWiki edit page. The
387 | // only indicator I found in the response XML is the "exists" attribute
388 | // in the templates node; but this may not be adequate.
389 | $html = null;
390 | if (isset($response['parse']['templates'][0]['exists'])) {
391 | $html = $response['parse']['text']['*'];
392 | }
393 | return $html;
394 | }
395 |
396 | /**
397 | * Get the HTML preview of the given text.
398 | *
399 | * @link http://www.mediawiki.org/wiki/API:Parsing_wikitext#parse
400 | * @param string $text
401 | * @return string
402 | */
403 | public function getPreview($text)
404 | {
405 | $response = $this->parse(array('text' => '__NOEDITSECTION__' . $text));
406 | return $response['parse']['text']['*'];
407 | }
408 |
409 | /**
410 | * Returns whether a given page is created.
411 | *
412 | * @link http://www.mediawiki.org/wiki/API:Query#Missing_and_invalid_titles
413 | * @param string $title
414 | * @return bool
415 | */
416 | public function pageCreated($title)
417 | {
418 | $response = $this->query(array('titles' => $title));
419 | $page = current($response['query']['pages']);
420 | if (isset($page['missing']) || isset($page['invalid'])) {
421 | return false;
422 | }
423 | return true;
424 | }
425 |
426 | /**
427 | * Returns parsed wikitext.
428 | *
429 | * @link http://www.mediawiki.org/wiki/API:Parsing_wikitext#parse
430 | * @param array $params
431 | * @return array
432 | */
433 | public function parse(array $params = array())
434 | {
435 | return $this->_request('parse', $params);
436 | }
437 |
438 | /**
439 | * Returns data.
440 | *
441 | * @link http://www.mediawiki.org/wiki/API:Query
442 | * @param array $params
443 | * @return array
444 | */
445 | public function query(array $params = array())
446 | {
447 | return $this->_request('query', $params);
448 | }
449 |
450 | /**
451 | * Watch or unwatch pages.
452 | *
453 | * @link http://www.mediawiki.org/wiki/API:Watch
454 | * @param string $title
455 | * @param array $params
456 | * @return array
457 | */
458 | public function watch($title, $watchtoken = null, array $params = array())
459 | {
460 | // Get the watch token if not passed.
461 | if (is_null($watchtoken)) {
462 | $watchtoken = $this->getWatchToken($title);
463 | }
464 | $params['title'] = $title;
465 | $params['token'] = $watchtoken;
466 | return $this->_request('watch', $params);
467 | }
468 |
469 | /**
470 | * Applies protections to a given page.
471 | *
472 | * @link http://www.mediawiki.org/wiki/API:Protect
473 | * @param string $title
474 | * @param string $protections
475 | * @param string|null $protecttokens
476 | * @param array $params
477 | * @return array
478 | */
479 | public function protect($title,
480 | $protections,
481 | $protecttoken = null,
482 | array $params = array())
483 | {
484 | // Get the protect token if not passed.
485 | if (is_null($protecttoken)) {
486 | $protecttoken = $this->getProtectToken($title);
487 | }
488 |
489 | // Apply protections.
490 | $params['title'] = $title;
491 | $params['protections'] = $protections;
492 | $params['token'] = $protecttoken;
493 |
494 | return $this->_request('protect', $params);
495 | }
496 |
497 | /**
498 | * Create or edit a given page.
499 | *
500 | * @link http://www.mediawiki.org/wiki/API:Edit
501 | * @link http://www.mediawiki.org/wiki/Manual:Preventing_access#Restrict_editing_of_all_pages
502 | * @param string $title
503 | * @param string $text
504 | * @param string|null $edittoken
505 | * @param array $params
506 | * @return array
507 | */
508 | public function edit($title,
509 | $text,
510 | $edittoken = null,
511 | array $params = array())
512 | {
513 | // Get the edit token if not passed.
514 | if (is_null($edittoken)) {
515 | $edittoken = $this->getEditToken($title);
516 | }
517 |
518 | // Protect against edit conflicts by getting the timestamp of the last
519 | // revision.
520 | $response = $this->getRevisions($title);
521 | $page = current($response['query']['pages']);
522 |
523 | $basetimestamp = null;
524 | if (isset($page['revisions'])) {
525 | $basetimestamp = $page['revisions'][0]['timestamp'];
526 | }
527 |
528 | // Edit the page.
529 | $params['title'] = $title;
530 | $params['text'] = $text;
531 | $params['token'] = $edittoken;
532 | $params['basetimestamp'] = $basetimestamp;
533 |
534 | return $this->_request('edit', $params);
535 | }
536 |
537 | /**
538 | * Login to MediaWiki.
539 | *
540 | * @link http://www.mediawiki.org/wiki/API:Login
541 | * @param string $lgname
542 | * @param string $lgpassword
543 | */
544 | public function login($lgname, $lgpassword)
545 | {
546 | // Log in or get the login token.
547 | $params = array('lgname' => $lgname, 'lgpassword' => $lgpassword);
548 | $response = $this->_request('login', $params);
549 |
550 | // Confirm the login token.
551 | if ('NeedToken' == $response['login']['result']) {
552 | $params['lgtoken'] = $response['login']['token'];
553 | $response = $this->_request('login', $params);
554 | }
555 |
556 | // Process a successful login.
557 | if ('Success' == $response['login']['result']) {
558 | if ($this->_passCookies) {
559 | $cookiePrefix = isset($response['login']['cookieprefix'])
560 | ? $response['login']['cookieprefix']
561 | : $this->_cookiePrefix;
562 | // Persist the MediaWiki cookie prefix in the browser. Set to
563 | // expire in 30 days, the same as MediaWiki cookies.
564 | setcookie(self::COOKIE_NS . 'cookieprefix',
565 | $cookiePrefix,
566 | time() + 60 * 60 * 24 * 30,
567 | '/');
568 |
569 | // Persist MediaWiki authentication cookies in the browser.
570 | foreach (self::getHttpClient()->getCookieJar()->getAllCookies() as $cookie) {
571 | setcookie(self::COOKIE_NS . $this->_cookiePrefix . $cookie->getName(),
572 | $cookie->getValue(),
573 | $cookie->getExpiryTime(),
574 | '/');
575 | }
576 | }
577 | return;
578 | }
579 |
580 | // Process an unsuccessful login.
581 | $errors = array('NoName' => 'Username is empty.',
582 | 'Illegal' => 'Username is illegal.',
583 | 'NotExists' => 'Username is not found.',
584 | 'EmptyPass' => 'Password is empty.',
585 | 'WrongPass' => 'Password is incorrect.',
586 | 'WrongPluginPass' => 'Password is incorrect (via plugin)',
587 | 'CreateBlocked' => 'IP address is blocked for account creation.',
588 | 'Throttled' => 'Login attempt limit surpassed.',
589 | 'Blocked' => 'User is blocked.');
590 | $error = $response['login']['result'];
591 | if (array_key_exists($error, $errors)) {
592 | throw new Scripto_Service_Exception($errors[$error]);
593 | }
594 | throw new Scripto_Service_Exception('Unknown login error: ' . $response['login']['result']);
595 | }
596 |
597 | /**
598 | * Logout of MediaWiki.
599 | *
600 | * @link http://www.mediawiki.org/wiki/API:Logout
601 | */
602 | public function logout()
603 | {
604 | // As of A CSRF token is required to log out as of MW 1.34.0.
605 | // @see https://github.com/wikimedia/mediawiki-api-demos/issues/118
606 | $response = $this->query(['meta' => 'tokens']);
607 | $token = $response['query']['tokens']['csrftoken'];
608 |
609 | // Log out.
610 | $this->_request('logout', ['token' => $token]);
611 |
612 | // Reset the cookie jar.
613 | self::getHttpClient()->getCookieJar()->reset();
614 |
615 | if ($this->_passCookies && $this->_cookiePrefix) {
616 | // Delete the MediaWiki authentication cookies from the browser.
617 | setcookie(self::COOKIE_NS . 'cookieprefix', false, 0, '/');
618 | foreach ($this->_cookieSuffixes as $cookieSuffix) {
619 | $cookieName = self::COOKIE_NS . $this->_cookiePrefix . $cookieSuffix;
620 | if (array_key_exists($cookieName, $_COOKIE)) {
621 | setcookie($cookieName, false, 0, '/');
622 | }
623 | }
624 | }
625 | }
626 |
627 | /**
628 | * Makes a MediaWiki API request and returns the response.
629 | *
630 | * @param string $action
631 | * @param array $params
632 | * @return array
633 | */
634 | protected function _request($action, array $params = array())
635 | {
636 | // Check if this action is a valid MediaWiki API action.
637 | if (!array_key_exists($action, $this->_actions)) {
638 | throw new Scripto_Service_Exception('Invalid MediaWiki API action.');
639 | }
640 |
641 | // Set valid parameters for this action.
642 | foreach ($params as $paramName => $paramValue) {
643 | if (in_array($paramName, $this->_actions[$action])) {
644 | self::getHttpClient()->setParameterPost($paramName, $paramValue);
645 | }
646 | }
647 |
648 | // Set default parameters.
649 | self::getHttpClient()->setParameterPost('format', 'json')
650 | ->setParameterPost('action', $action);
651 |
652 | // Get the response body and reset the request.
653 | $body = self::getHttpClient()->request('POST')->getBody();
654 | self::getHttpClient()->resetParameters();
655 |
656 | // Parse the response body, throwing errors when encountered.
657 | $response = json_decode($body, true);
658 | if (isset($response['error'])) {
659 | throw new Scripto_Service_Exception($response['error']['info']);
660 | }
661 | return $response;
662 | }
663 |
664 | /**
665 | * Determine whether the provided MediaWiki API URL is valid.
666 | *
667 | * @param string $apiUrl
668 | * @return bool
669 | */
670 | static public function isValidApiUrl($apiUrl)
671 | {
672 | // Check for valid API URL string.
673 | if (!Zend_Uri::check($apiUrl) || !preg_match('#/api\.php$#', $apiUrl)) {
674 | return false;
675 | }
676 |
677 | try {
678 | // Ping the API endpoint for a valid response.
679 | $body = self::getHttpClient()->setUri($apiUrl)
680 | ->setParameterPost('action', 'query')
681 | ->setParameterPost('meta', 'siteinfo')
682 | ->setParameterPost('format', 'json')
683 | ->request('POST')->getBody();
684 | // Prevent "Unable to Connect" errors.
685 | } catch (Zend_Http_Client_Exception $e) {
686 | return false;
687 | }
688 | self::getHttpClient()->resetParameters(true);
689 |
690 | $response = json_decode($body, true);
691 | if (!is_array($response) || !isset($response['query']['general'])) {
692 | return false;
693 | }
694 |
695 | return true;
696 | }
697 | }
698 |
--------------------------------------------------------------------------------
/models/ScriptoAdapterOmeka.php:
--------------------------------------------------------------------------------
1 | _db = get_db();
18 | }
19 |
20 | /**
21 | * Indicate whether the document exists in Omeka.
22 | *
23 | * @param int|string $documentId The unique document ID
24 | * @return bool True: it exists; false: it does not exist
25 | */
26 | public function documentExists($documentId)
27 | {
28 | return $this->_validDocument($this->_getItem($documentId));
29 | }
30 |
31 | /**
32 | * Indicate whether the document page exists in Omeka.
33 | *
34 | * @param int|string $documentId The unique document ID
35 | * @param int|string $pageId The unique page ID
36 | * @return bool True: it exists; false: it does not exist
37 | */
38 | public function documentPageExists($documentId, $pageId)
39 | {
40 | $item = $this->_getItem($documentId);
41 | if (false == $this->_validDocument($item)) {
42 | return false;
43 | }
44 | // The Omeka file ID must match the Scripto page ID.
45 | $files = $item->Files;
46 | foreach ($files as $file) {
47 | if ($pageId == $file->id) {
48 | return true;
49 | }
50 | }
51 | return false;
52 | }
53 |
54 | /**
55 | * Get all the pages belonging to the document.
56 | *
57 | * @param int|string $documentId The unique document ID
58 | * @return array An array containing page identifiers as keys and page names
59 | * as values, in sequential page order.
60 | */
61 | public function getDocumentPages($documentId)
62 | {
63 | $item = $this->_getItem($documentId);
64 | $documentPages = array();
65 | foreach ($item->Files as $file) {
66 | // The page name is either the Dublin Core title of the file or the
67 | // file's original filename.
68 | $titles = $file->getElementTexts('Dublin Core', 'Title');
69 | if (empty($titles)) {
70 | $pageName = $file->original_filename;
71 | } else {
72 | $pageName = $titles[0]->text;
73 | }
74 | $documentPages[$file->id] = $pageName;
75 | }
76 | return $documentPages;
77 | }
78 |
79 | /**
80 | * Get the URL of the specified document page file.
81 | *
82 | * @param int|string $documentId The unique document ID
83 | * @param int|string $pageId The unique page ID
84 | * @return string The page file URL
85 | */
86 | public function getDocumentPageFileUrl($documentId, $pageId)
87 | {
88 | $file = $this->_getFile($pageId);
89 | return $file->getWebPath('original');
90 | }
91 |
92 | /**
93 | * Get the first page of the document.
94 | *
95 | * @param int|string $documentId The document ID
96 | * @return int|string
97 | */
98 | public function getDocumentFirstPageId($documentId)
99 | {
100 | $item = $this->_getItem($documentId);
101 | return $item->Files[0]->id;
102 | }
103 |
104 | /**
105 | * Get the title of the document.
106 | *
107 | * @param int|string $documentId The document ID
108 | * @return string
109 | */
110 | public function getDocumentTitle($documentId)
111 | {
112 | $item = $this->_getItem($documentId);
113 | $titles = $item->getElementTexts('Dublin Core', 'Title');
114 | if (empty($titles)) {
115 | return '';
116 | }
117 | return $titles[0]->text;
118 | }
119 |
120 | /**
121 | * Get the name of the document page.
122 | *
123 | * @param int|string $documentId The document ID
124 | * @param int|string $pageId The unique page ID
125 | * @return string
126 | */
127 | public function getDocumentPageName($documentId, $pageId)
128 | {
129 | $file = $this->_getFile($pageId);
130 |
131 | // The page name is either the Dublin Core title of the file or the
132 | // file's original filename.
133 | $titles = $file->getElementTexts('Dublin Core', 'Title');
134 | if (empty($titles)) {
135 | $pageName = $file->original_filename;
136 | } else {
137 | $pageName = $titles[0]->text;
138 | }
139 | return $pageName;
140 | }
141 |
142 | /**
143 | * Indicate whether the document transcription has been imported.
144 | *
145 | * @param int|string $documentId The document ID
146 | * @return bool True: has been imported; false: has not been imported
147 | */
148 | public function documentTranscriptionIsImported($documentId)
149 | {}
150 |
151 | /**
152 | * Indicate whether the document page transcription has been imported.
153 | *
154 | * @param int|string $documentId The document ID
155 | * @param int|string $pageId The page ID
156 | */
157 | public function documentPageTranscriptionIsImported($documentId, $pageId)
158 | {}
159 |
160 | /**
161 | * Import a document page's transcription into Omeka.
162 | *
163 | * @param int|string $documentId The document ID
164 | * @param int|string $pageId The page ID
165 | * @param string $text The text to import
166 | * @return bool True: success; false: fail
167 | */
168 | public function importDocumentPageTranscription($documentId, $pageId, $text)
169 | {
170 | $file = $this->_getFile($pageId);
171 | $element = $file->getElement('Scripto', 'Transcription');
172 | $file->deleteElementTextsByElementId(array($element->id));
173 | $isHtml = false;
174 | if ('html' == get_option('scripto_import_type')) {
175 | $isHtml = true;
176 | }
177 | $text = Scripto::removeNewPPLimitReports($text);
178 | $file->addTextForElement($element, $text, $isHtml);
179 | $file->save();
180 | }
181 |
182 | /**
183 | * Import an entire document's transcription into Omeka.
184 | *
185 | * @param int|string The document ID
186 | * @param string The text to import
187 | * @return bool True: success; false: fail
188 | */
189 | public function importDocumentTranscription($documentId, $text)
190 | {
191 | $item = $this->_getItem($documentId);
192 | $element = $item->getElement('Scripto', 'Transcription');
193 | $item->deleteElementTextsByElementId(array($element->id));
194 | $isHtml = false;
195 | if ('html' == get_option('scripto_import_type')) {
196 | $isHtml = true;
197 | }
198 | $text = Scripto::removeNewPPLimitReports($text);
199 | $item->addTextForElement($element, $text, $isHtml);
200 | $item->save();
201 | }
202 |
203 | /**
204 | * Return an Omeka item object.
205 | *
206 | * @param int $itemId
207 | * @return Item|null
208 | */
209 | private function _getItem($itemId)
210 | {
211 | return $this->_db->getTable('Item')->find($itemId);
212 | }
213 |
214 | /**
215 | * Return an Omeka file object.
216 | *
217 | * @param int $fileId
218 | * @return File|int
219 | */
220 | private function _getFile($fileId)
221 | {
222 | return $this->_db->getTable('File')->find($fileId);
223 | }
224 |
225 | /**
226 | * Check if the provided item exists in Omeka and is a valid Scripto
227 | * document.
228 | *
229 | * @param Item $item
230 | * @return bool
231 | */
232 | private function _validDocument($item)
233 | {
234 | // The item must exist.
235 | if (!($item instanceof Item)) {
236 | return false;
237 | }
238 | // The item must have at least one file assigned to it.
239 | if (!isset($item->Files[0])) {
240 | return false;
241 | }
242 | return true;
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/plugin.ini:
--------------------------------------------------------------------------------
1 | [info]
2 | name="Scripto"
3 | author="Roy Rosenzweig Center for History and New Media"
4 | description="Adds the ability to transcribe items using the Scripto library."
5 | license="GPLv3"
6 | link="http://omeka.org/codex/Plugins/Scripto_2.0"
7 | support_link="https://forum.omeka.org/c/omeka-classic/plugins"
8 | version="2.5"
9 | omeka_minimum_version="2.0"
10 | omeka_target_version="2.4"
11 | tags="scripto, transcription, crowdsource, documents"
12 |
--------------------------------------------------------------------------------
/routes.ini:
--------------------------------------------------------------------------------
1 | [routes]
2 | scripto_action.route = "scripto/:action"
3 | scripto_action.defaults.module = "scripto"
4 | scripto_action.defaults.controller = "index"
5 |
6 | scripto_action_item.route = "scripto/:action/:item-id"
7 | scripto_action_item.defaults.module = "scripto"
8 | scripto_action_item.defaults.controller = "index"
9 | scripto_action_item.reqs.item-id = "\d+"
10 |
11 | scripto_action_item_file.route = "scripto/:action/:item-id/:file-id"
12 | scripto_action_item_file.defaults.module = "scripto"
13 | scripto_action_item_file.defaults.controller = "index"
14 | scripto_action_item_file.reqs.item-id = "\d+"
15 | scripto_action_item_file.reqs.file-id = "\d+"
16 |
17 | scripto_revision.route = "scripto/revision/:item-id/:file-id/:namespace-index/:revision-id"
18 | scripto_revision.defaults.module = "scripto"
19 | scripto_revision.defaults.controller = "index"
20 | scripto_revision.defaults.action = "revision"
21 | scripto_revision.reqs.item-id = "\d+"
22 | scripto_revision.reqs.file-id = "\d+"
23 | scripto_revision.reqs.namespace-index = "0|1"
24 | scripto_revision.reqs.revision-id = "\d+"
25 |
26 | scripto_history.route = "scripto/history/:item-id/:file-id/:namespace-index"
27 | scripto_history.defaults.module = "scripto"
28 | scripto_history.defaults.controller = "index"
29 | scripto_history.defaults.action = "history"
30 | scripto_history.reqs.item-id = "\d+"
31 | scripto_history.reqs.file-id = "\d+"
32 | scripto_history.reqs.namespace-index = "0|1"
33 |
34 | scripto_diff.route = "scripto/diff/:item-id/:file-id/:namespace-index/:old-revision-id/:revision-id"
35 | scripto_diff.defaults.module = "scripto"
36 | scripto_diff.defaults.controller = "index"
37 | scripto_diff.defaults.action = "diff"
38 | scripto_diff.reqs.item-id = "\d+"
39 | scripto_diff.reqs.file-id = "\d+"
40 | scripto_diff.reqs.namespace-index = "0|1"
41 | scripto_diff.reqs.old-revision-id = "\d+"
42 | scripto_diff.reqs.revision-id = "\d+"
43 |
--------------------------------------------------------------------------------
/views/admin/plugins/scripto-config-form.php:
--------------------------------------------------------------------------------
1 | ', ''
7 | ); ?>
8 |
9 |
12 |
13 |
28 |
29 |
45 |
46 |
66 |
67 |
85 |
86 |
87 |
88 |
89 |
90 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
125 |
126 |
--------------------------------------------------------------------------------
/views/shared/index/diff.php:
--------------------------------------------------------------------------------
1 | namespaceIndex) ? __('Discussion') : __('Transcription');
4 | $title = implode(' | ', $titleArray);
5 | $head = array('title' => html_escape($title));
6 | echo head($head);
7 | ?>
8 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | scripto->isLoggedIn()): ?>
27 | ' . $this->scripto->getUserName() . ''); ?>
28 | ( )
29 | |
30 |
31 |
32 |
33 | |
34 | |
35 | |
36 | |
37 | |
38 |
39 |
40 |
doc->getTitle()): ?>doc->getTitle(); ?>
41 |
doc->getPageName(); ?>
42 |
43 |
44 |
45 |
46 |
47 | oldRevision['timestamp']), Zend_Date::DATETIME_MEDIUM)); ?>
48 | oldRevision['action'] . ' by'); ?> oldRevision['user']; ?>
49 | revision['timestamp']), Zend_Date::DATETIME_MEDIUM)); ?>
50 | revision['action'] . ' by'); ?> revision['user']; ?>
51 |
52 |
53 |
54 | diff; ?>
55 |
56 |
57 |
revision['timestamp']), Zend_Date::DATETIME_MEDIUM)); ?>
58 |
revision['html']; ?>
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/views/shared/index/history.php:
--------------------------------------------------------------------------------
1 | namespaceIndex) ? __('Discussion') : __('Transcription');
4 | $title = implode(' | ', $titleArray);
5 | $head = array('title' => html_escape($title));
6 | echo head($head);
7 | ?>
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | scripto->isLoggedIn()): ?>
18 | ' . $this->scripto->getUserName() . ''); ?>
19 | ( )
20 | |
21 |
22 |
23 |
24 | |
25 | |
26 | |
27 | |
28 |
29 |
30 |
doc->getTitle()): ?>doc->getTitle(); ?>
31 |
doc->getPageName(); ?>
32 |
33 |
34 | history)): ?>
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | history as $revision): ?>
49 | $this->doc->getId(),
51 | 'file-id' => $this->doc->getPageId(),
52 | 'namespace-index' => $this->namespaceIndex,
53 | 'old-revision-id' => $revision['revision_id'],
54 | 'revision-id' => $this->info['last_revision_id']),
55 | 'scripto_diff');
56 | $urlPrevious = url(array('item-id' => $this->doc->getId(),
57 | 'file-id' => $this->doc->getPageId(),
58 | 'namespace-index' => $this->namespaceIndex,
59 | 'old-revision-id' => $revision['parent_id'],
60 | 'revision-id' => $revision['revision_id']),
61 | 'scripto_diff');
62 | $urlRevert = url(array('item-id' => $this->doc->getId(),
63 | 'file-id' => $this->doc->getPageId(),
64 | 'namespace-index' => $this->namespaceIndex,
65 | 'revision-id' => $revision['revision_id']),
66 | 'scripto_revision');
67 | ?>
68 |
69 | (info['last_revision_id']): ?> | )
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/views/shared/index/index.php:
--------------------------------------------------------------------------------
1 | html_escape(__('Scripto')));
3 | echo head($head);
4 | ?>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | scripto->isLoggedIn()): ?>
15 | scripto->getUserName()); ?>
16 | ( )
17 | |
18 |
19 |
20 |
21 | |
22 |
23 |
24 |
25 | scripto->isLoggedIn()): ?>
26 | homePageText): ?>
27 | homePageText ?>
28 |
29 |
30 |
' . get_option('site_title') . '',
38 | '', ' ',
39 | '', ' ',
40 | '', ' ',
41 | '', ' ',
42 | '', ' '
43 | ); ?>
44 |
45 |
46 |
47 | documentPages)): ?>
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | documentPages as $documentPage): ?>
60 | 'transcribe',
65 | 'item-id' => $documentPage['document_id'],
66 | 'file-id' => $documentPage['document_page_id']
67 | ), 'scripto_action_item_file');
68 | if (1 == $documentPage['namespace_index']) {
69 | $urlTranscribe .= '#discussion';
70 | } else {
71 | $urlTranscribe .= '#transcription';
72 | }
73 |
74 | // document title
75 | $documentTitle = ScriptoPlugin::truncate($documentPage['document_title'], 60, __('Untitled'));
76 | $urlItem = url(array(
77 | 'controller' => 'items',
78 | 'action' => 'show',
79 | 'id' => $documentPage['document_id']
80 | ), 'id');
81 | ?>
82 |
83 | :
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/views/shared/index/login.php:
--------------------------------------------------------------------------------
1 | html_escape($title));
5 | echo head($head);
6 | ?>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/views/shared/index/recent-changes.php:
--------------------------------------------------------------------------------
1 | html_escape(__('Scripto')));
3 | echo head($head);
4 | ?>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | scripto->isLoggedIn()): ?>
15 | ' . $this->scripto->getUserName() . ''); ?>
16 | ( )
17 | |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | recentChanges)): ?>
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 'Created', 'edit' => 'Edited'); ?>
41 | recentChanges as $recentChange): ?>
42 | $recentChange['document_id'],
46 | 'file-id' => $recentChange['document_page_id'],
47 | 'namespace-index' => $recentChange['namespace_index'],
48 | 'old-revision-id' => $recentChange['old_revision_id'],
49 | 'revision-id' => $recentChange['revision_id'],
50 | ), 'scripto_diff');
51 | $urlHistory = url(array(
52 | 'item-id' => $recentChange['document_id'],
53 | 'file-id' => $recentChange['document_page_id'],
54 | 'namespace-index' => $recentChange['namespace_index'],
55 | ), 'scripto_history');
56 | if ($recentChange['new'] || in_array($recentChange['action'], array('protected', 'unprotected'))) {
57 | $changes .= ' (' . __('diff') . ' | ' . __('hist') . ' )';
58 | } else {
59 | $changes .= ' (' . __('diff') . ' | ' . __('hist') . ' )';
60 | }
61 |
62 | // document page name
63 | $documentPageName = ScriptoPlugin::truncate($recentChange['document_page_name'], 30);
64 | $urlTranscribe = url(array(
65 | 'action' => 'transcribe',
66 | 'item-id' => $recentChange['document_id'],
67 | 'file-id' => $recentChange['document_page_id']
68 | ), 'scripto_action_item_file');
69 | if (1 == $recentChange['namespace_index']) {
70 | $urlTranscribe .= '#discussion';
71 | } else {
72 | $urlTranscribe .= '#transcription';
73 | }
74 |
75 | // document title
76 | $documentTitle = ScriptoPlugin::truncate($recentChange['document_title'], 30, __('Untitled'));
77 | $urlItem = url(array(
78 | 'controller' => 'items',
79 | 'action' => 'show',
80 | 'id' => $recentChange['document_id']
81 | ), 'id');
82 |
83 | // length changed
84 | $lengthChanged = $recentChange['new_length'] - $recentChange['old_length'];
85 | if (0 <= $lengthChanged) {
86 | $lengthChanged = "+$lengthChanged";
87 | }
88 | ?>
89 |
90 |
91 | :
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/views/shared/index/revision.php:
--------------------------------------------------------------------------------
1 | namespaceIndex) ? __('Discussion') : __('Transcription');
4 | $title = implode(' | ', $titleArray);
5 | $head = array('title' => html_escape($title));
6 | echo head($head);
7 | ?>
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | scripto->isLoggedIn()): ?>
18 | ' . $this->scripto->getUserName() . ''); ?>
19 | ( )
20 | |
21 |
22 |
23 |
24 | |
25 | |
26 | |
27 | |
28 | |
29 |
30 |
31 |
doc->getTitle()): ?>doc->getTitle(); ?>
32 |
doc->getPageName(); ?>
33 |
34 |
35 | namespaceIndex && $this->doc->canEditTalkPage()): ?>
36 |
formTextarea('scripto-page-wikitext', $this->revision['wikitext'], array('cols' => '76', 'rows' => '16', 'disabled' => 'disabled')); ?>
37 |
40 | doc->canEditTranscriptionPage()): ?>
41 |
formTextarea('scripto-page-wikitext', $this->revision['wikitext'], array('cols' => '76', 'rows' => '16', 'disabled' => 'disabled')); ?>
42 |
45 |
46 |
47 |
48 |
revision['timestamp']), Zend_Date::DATETIME_MEDIUM),
51 | __($this->revision['action'] . ' by'),
52 | $this->revision['user']
53 | ); ?>
54 |
revision['html']; ?>
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/views/shared/index/transcribe.php:
--------------------------------------------------------------------------------
1 | html_escape(implode(' | ', $titleArray)));
4 | echo head($head);
5 | ?>
6 |
7 |
8 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 | scripto->isLoggedIn()): ?>
341 | ' . $this->scripto->getUserName() . ''); ?>
342 | ( )
343 | |
344 |
345 |
346 |
347 | |
348 | |
349 | |
350 |
351 |
352 |
doc->getTitle()): ?>doc->getTitle(); ?>
353 | scripto->canExport()): ?>
formButton('scripto-transcription-document-import', __('Import document'), array('style' => 'display:inline; float:none;')); ?>
354 |
doc->getPageName(); ?>
355 |
356 |
357 | file, array('imageSize' => 'fullsize')); ?>
358 |
359 |
360 |
361 | paginationUrls['previous'])): ?>« «
362 | | paginationUrls['next'])): ?> » »
363 | |
364 |
365 |
366 |
367 |
368 | doc->canEditTranscriptionPage()): ?>
369 |
370 |
formTextarea('scripto-transcription-page-wikitext', $this->doc->getTranscriptionPageWikitext(), array('cols' => '76', 'rows' => '16')); ?>
371 |
372 | formButton('scripto-transcription-page-edit', __('Edit transcription'), array('style' => 'display:inline; float:none;')); ?>
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 | doc->canEditTranscriptionPage()): ?> [ ]
381 | scripto->canProtect()): ?> [ ]
382 | [ ]
383 |
384 | scripto->isLoggedIn()): ?>formButton('scripto-page-watch'); ?>
385 | scripto->canProtect()): ?>formButton('scripto-transcription-page-protect'); ?>
386 | scripto->canExport()): ?>formButton('scripto-transcription-page-import', __('Import page'), array('style' => 'display:inline; float:none;')); ?>
387 |
388 |
transcriptionPageHtml; ?>
389 |
390 |
391 |
392 |
393 | doc->canEditTalkPage()): ?>
394 |
395 |
formTextarea('scripto-talk-page-wikitext', $this->doc->getTalkPageWikitext(), array('cols' => '76', 'rows' => '16')); ?>
396 |
397 | formButton('scripto-talk-page-edit', __('Edit discussion'), array('style' => 'display:inline; float:none;')); ?>
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 | doc->canEditTalkPage()): ?> [ ]
406 | scripto->canProtect()): ?> [ ]
407 | [ ]
408 |
409 | scripto->canProtect()): ?>formButton('scripto-talk-page-protect'); ?>
410 |
411 |
talkPageHtml; ?>
412 |
413 |
414 |
415 |
416 |
417 |
--------------------------------------------------------------------------------
/views/shared/index/watchlist.php:
--------------------------------------------------------------------------------
1 | html_escape(__('Scripto')));
3 | echo head($head);
4 | ?>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ' . $this->scripto->getUserName() . ''); ?>
15 | ( )
16 | |
17 |
18 |
19 |
20 |
21 | watchlist)): ?>
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | watchlist as $revision): ?>
37 | $revision['document_id'],
42 | 'file-id' => $revision['document_page_id'],
43 | 'namespace-index' => $revision['namespace_index'],
44 | ), 'scripto_history');
45 | $changes .= ' (' . __('hist') . ' )';
46 |
47 | // document page name
48 | $documentPageName = ScriptoPlugin::truncate($revision['document_page_name'], 30);
49 | $urlTranscribe = url(array(
50 | 'action' => 'transcribe',
51 | 'item-id' => $revision['document_id'],
52 | 'file-id' => $revision['document_page_id']
53 | ), 'scripto_action_item_file');
54 | if (1 == $revision['namespace_index']) {
55 | $urlTranscribe .= '#discussion';
56 | } else {
57 | $urlTranscribe .= '#transcription';
58 | }
59 |
60 | // document title
61 | $documentTitle = ScriptoPlugin::truncate($revision['document_title'], 30, __('Untitled'));
62 | $urlItem = url(array(
63 | 'controller' => 'items',
64 | 'action' => 'show',
65 | 'id' => $revision['document_id']
66 | ), 'id');
67 |
68 | // length changed
69 | $lengthChanged = $revision['new_length'] - $revision['old_length'];
70 | if (0 <= $lengthChanged) {
71 | $lengthChanged = "+$lengthChanged";
72 | }
73 | ?>
74 |
75 |
76 | :
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/views/shared/javascripts/img/east-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omeka/plugin-Scripto/c84b77e7e5a057876b4c070aeddf7e7296ba5851/views/shared/javascripts/img/east-mini.png
--------------------------------------------------------------------------------
/views/shared/javascripts/img/north-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omeka/plugin-Scripto/c84b77e7e5a057876b4c070aeddf7e7296ba5851/views/shared/javascripts/img/north-mini.png
--------------------------------------------------------------------------------
/views/shared/javascripts/img/south-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omeka/plugin-Scripto/c84b77e7e5a057876b4c070aeddf7e7296ba5851/views/shared/javascripts/img/south-mini.png
--------------------------------------------------------------------------------
/views/shared/javascripts/img/west-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omeka/plugin-Scripto/c84b77e7e5a057876b4c070aeddf7e7296ba5851/views/shared/javascripts/img/west-mini.png
--------------------------------------------------------------------------------
/views/shared/javascripts/img/zoom-minus-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omeka/plugin-Scripto/c84b77e7e5a057876b4c070aeddf7e7296ba5851/views/shared/javascripts/img/zoom-minus-mini.png
--------------------------------------------------------------------------------
/views/shared/javascripts/img/zoom-plus-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omeka/plugin-Scripto/c84b77e7e5a057876b4c070aeddf7e7296ba5851/views/shared/javascripts/img/zoom-plus-mini.png
--------------------------------------------------------------------------------
/views/shared/javascripts/img/zoom-world-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omeka/plugin-Scripto/c84b77e7e5a057876b4c070aeddf7e7296ba5851/views/shared/javascripts/img/zoom-world-mini.png
--------------------------------------------------------------------------------
/views/shared/javascripts/theme/default/style.css:
--------------------------------------------------------------------------------
1 | div.olMap {
2 | z-index: 0;
3 | padding: 0px!important;
4 | margin: 0px!important;
5 | cursor: default;
6 | }
7 |
8 | div.olMapViewport {
9 | text-align: left;
10 | }
11 |
12 | div.olLayerDiv {
13 | -moz-user-select: none;
14 | }
15 |
16 | .olLayerGoogleCopyright {
17 | left: 2px;
18 | bottom: 2px;
19 | }
20 | .olLayerGooglePoweredBy {
21 | left: 2px;
22 | bottom: 15px;
23 | }
24 | .olControlAttribution {
25 | font-size: smaller;
26 | right: 3px;
27 | bottom: 4.5em;
28 | position: absolute;
29 | display: block;
30 | }
31 | .olControlScale {
32 | right: 3px;
33 | bottom: 3em;
34 | display: block;
35 | position: absolute;
36 | font-size: smaller;
37 | }
38 | .olControlScaleLine {
39 | display: block;
40 | position: absolute;
41 | left: 10px;
42 | bottom: 15px;
43 | font-size: xx-small;
44 | }
45 | .olControlScaleLineBottom {
46 | border: solid 2px black;
47 | border-bottom: none;
48 | margin-top:-2px;
49 | text-align: center;
50 | }
51 | .olControlScaleLineTop {
52 | border: solid 2px black;
53 | border-top: none;
54 | text-align: center;
55 | }
56 |
57 | .olControlPermalink {
58 | right: 3px;
59 | bottom: 1.5em;
60 | display: block;
61 | position: absolute;
62 | font-size: smaller;
63 | }
64 |
65 | div.olControlMousePosition {
66 | bottom: 0em;
67 | right: 3px;
68 | display: block;
69 | position: absolute;
70 | font-family: Arial;
71 | font-size: smaller;
72 | }
73 |
74 | .olControlOverviewMapContainer {
75 | position: absolute;
76 | bottom: 0px;
77 | right: 0px;
78 | }
79 |
80 | .olControlOverviewMapElement {
81 | padding: 10px 18px 10px 10px;
82 | background-color: #00008B;
83 | -moz-border-radius: 1em 0 0 0;
84 | }
85 |
86 | .olControlOverviewMapMinimizeButton {
87 | right: 0px;
88 | bottom: 80px;
89 | }
90 |
91 | .olControlOverviewMapMaximizeButton {
92 | right: 0px;
93 | bottom: 80px;
94 | }
95 |
96 | .olControlOverviewMapExtentRectangle {
97 | overflow: hidden;
98 | background-image: url("img/blank.gif");
99 | cursor: move;
100 | border: 2px dotted red;
101 | }
102 | .olControlOverviewMapRectReplacement {
103 | overflow: hidden;
104 | cursor: move;
105 | background-image: url("img/overview_replacement.gif");
106 | background-repeat: no-repeat;
107 | background-position: center;
108 | }
109 |
110 | .olLayerGeoRSSDescription {
111 | float:left;
112 | width:100%;
113 | overflow:auto;
114 | font-size:1.0em;
115 | }
116 | .olLayerGeoRSSClose {
117 | float:right;
118 | color:gray;
119 | font-size:1.2em;
120 | margin-right:6px;
121 | font-family:sans-serif;
122 | }
123 | .olLayerGeoRSSTitle {
124 | float:left;font-size:1.2em;
125 | }
126 |
127 | .olPopupContent {
128 | padding:5px;
129 | overflow: auto;
130 | }
131 | .olControlNavToolbar {
132 | width:0px;
133 | height:0px;
134 | }
135 | .olControlNavToolbar div {
136 | display:block;
137 | width: 28px;
138 | height: 28px;
139 | top: 300px;
140 | left: 6px;
141 | position: relative;
142 | }
143 |
144 | .olControlNavigationHistory {
145 | background-image: url("img/navigation_history.png");
146 | background-repeat: no-repeat;
147 | width: 24px;
148 | height: 24px;
149 |
150 | }
151 | .olControlNavigationHistoryPreviousItemActive {
152 | background-position: 0px 0px;
153 | }
154 | .olControlNavigationHistoryPreviousItemInactive {
155 | background-position: 0px -24px;
156 | }
157 | .olControlNavigationHistoryNextItemActive {
158 | background-position: -24px 0px;
159 | }
160 | .olControlNavigationHistoryNextItemInactive {
161 | background-position: -24px -24px;
162 | }
163 |
164 | .olControlNavToolbar .olControlNavigationItemActive {
165 | background-image: url("img/panning-hand-on.png");
166 | background-repeat: no-repeat;
167 | }
168 | .olControlNavToolbar .olControlNavigationItemInactive {
169 | background-image: url("img/panning-hand-off.png");
170 | background-repeat: no-repeat;
171 | }
172 | .olControlNavToolbar .olControlZoomBoxItemActive {
173 | background-image: url("img/drag-rectangle-on.png");
174 | background-color: orange;
175 | background-repeat: no-repeat;
176 | }
177 | .olControlNavToolbar .olControlZoomBoxItemInactive {
178 | background-image: url("img/drag-rectangle-off.png");
179 | background-repeat: no-repeat;
180 | }
181 | .olControlEditingToolbar {
182 | float:right;
183 | right: 0px;
184 | height: 30px;
185 | width: 200px;
186 | }
187 | .olControlEditingToolbar div {
188 | background-image: url("img/editing_tool_bar.png");
189 | background-repeat: no-repeat;
190 | float:right;
191 | width: 24px;
192 | height: 24px;
193 | margin: 5px;
194 | }
195 | .olControlEditingToolbar .olControlNavigationItemActive {
196 | background-position: -103px -23px;
197 | }
198 | .olControlEditingToolbar .olControlNavigationItemInactive {
199 | background-position: -103px -0px;
200 | }
201 | .olControlEditingToolbar .olControlDrawFeaturePointItemActive {
202 | background-position: -77px -23px;
203 | }
204 | .olControlEditingToolbar .olControlDrawFeaturePointItemInactive {
205 | background-position: -77px -0px;
206 | }
207 | .olControlEditingToolbar .olControlDrawFeaturePathItemInactive {
208 | background-position: -51px 0px;
209 | }
210 | .olControlEditingToolbar .olControlDrawFeaturePathItemActive {
211 | background-position: -51px -23px;
212 | }
213 | .olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive {
214 | background-position: -26px 0px;
215 | }
216 | .olControlEditingToolbar .olControlDrawFeaturePolygonItemActive {
217 | background-position: -26px -23px ;
218 | }
219 | div.olControlSaveFeaturesItemActive {
220 | background-image: url(img/save_features_on.png);
221 | background-repeat: no-repeat;
222 | background-position: 0px 1px;
223 | }
224 | div.olControlSaveFeaturesItemInactive {
225 | background-image: url(img/save_features_off.png);
226 | background-repeat: no-repeat;
227 | background-position: 0px 1px;
228 | }
229 |
230 | .olHandlerBoxZoomBox {
231 | border: 2px solid red;
232 | position: absolute;
233 | background-color: white;
234 | opacity: 0.50;
235 | font-size: 1px;
236 | filter: alpha(opacity=50);
237 | }
238 | .olHandlerBoxSelectFeature {
239 | border: 2px solid blue;
240 | position: absolute;
241 | background-color: white;
242 | opacity: 0.50;
243 | font-size: 1px;
244 | filter: alpha(opacity=50);
245 | }
246 |
247 | .olControlPanPanel {
248 | top: 10px;
249 | left: 5px;
250 | }
251 |
252 | .olControlPanPanel div {
253 | background-image: url(img/pan-panel.png);
254 | height: 18px;
255 | width: 18px;
256 | cursor: pointer;
257 | position: absolute;
258 | }
259 |
260 | .olControlPanPanel .olControlPanNorthItemInactive {
261 | top: 0px;
262 | left: 9px;
263 | background-position: 0px 0px;
264 | }
265 | .olControlPanPanel .olControlPanSouthItemInactive {
266 | top: 36px;
267 | left: 9px;
268 | background-position: 18px 0px;
269 | }
270 | .olControlPanPanel .olControlPanWestItemInactive {
271 | position: absolute;
272 | top: 18px;
273 | left: 0px;
274 | background-position: 0px 18px;
275 | }
276 | .olControlPanPanel .olControlPanEastItemInactive {
277 | top: 18px;
278 | left: 18px;
279 | background-position: 18px 18px;
280 | }
281 |
282 | .olControlZoomPanel {
283 | top: 71px;
284 | left: 14px;
285 | }
286 |
287 | .olControlZoomPanel div {
288 | background-image: url(img/zoom-panel.png);
289 | position: absolute;
290 | height: 18px;
291 | width: 18px;
292 | cursor: pointer;
293 | }
294 |
295 | .olControlZoomPanel .olControlZoomInItemInactive {
296 | top: 0px;
297 | left: 0px;
298 | background-position: 0px 0px;
299 | }
300 |
301 | .olControlZoomPanel .olControlZoomToMaxExtentItemInactive {
302 | top: 18px;
303 | left: 0px;
304 | background-position: 0px -18px;
305 | }
306 |
307 | .olControlZoomPanel .olControlZoomOutItemInactive {
308 | top: 36px;
309 | left: 0px;
310 | background-position: 0px 18px;
311 | }
312 |
313 | .olPopupCloseBox {
314 | background: url("img/close.gif") no-repeat;
315 | cursor: pointer;
316 | }
317 |
318 | .olFramedCloudPopupContent {
319 | padding: 5px;
320 | overflow: auto;
321 | }
322 |
323 | .olControlNoSelect {
324 | -moz-user-select: none;
325 | }
326 |
327 | .olImageLoadError {
328 | background-color: pink;
329 | opacity: 0.5;
330 | filter: alpha(opacity=50); /* IE */
331 | }
332 |
333 | /**
334 | * Cursor styles
335 | */
336 |
337 | .olCursorWait {
338 | cursor: wait;
339 | }
340 | .olDragDown {
341 | cursor: move;
342 | }
343 | .olDrawBox {
344 | cursor: crosshair;
345 | }
346 | .olControlDragFeatureOver {
347 | cursor: move;
348 | }
349 | .olControlDragFeatureActive.olControlDragFeatureOver.olDragDown {
350 | cursor: -moz-grabbing;
351 | }
352 |
353 | /**
354 | * Layer switcher
355 | */
356 | .olControlLayerSwitcher {
357 | position: absolute;
358 | top: 25px;
359 | right: 0px;
360 | width: 20em;
361 | font-family: sans-serif;
362 | font-weight: bold;
363 | margin-top: 3px;
364 | margin-left: 3px;
365 | margin-bottom: 3px;
366 | font-size: smaller;
367 | color: white;
368 | background-color: transparent;
369 | }
370 |
371 | .olControlLayerSwitcher .layersDiv {
372 | padding-top: 5px;
373 | padding-left: 10px;
374 | padding-bottom: 5px;
375 | padding-right: 75px;
376 | background-color: darkblue;
377 | width: 100%;
378 | height: 100%;
379 | }
380 |
381 | .olControlLayerSwitcher .layersDiv .baseLbl,
382 | .olControlLayerSwitcher .layersDiv .dataLbl {
383 | margin-top: 3px;
384 | margin-left: 3px;
385 | margin-bottom: 3px;
386 | }
387 |
388 | .olControlLayerSwitcher .layersDiv .baseLayersDiv,
389 | .olControlLayerSwitcher .layersDiv .dataLayersDiv {
390 | padding-left: 10px;
391 | }
392 |
393 | .olControlLayerSwitcher .maximizeDiv,
394 | .olControlLayerSwitcher .minimizeDiv {
395 | top: 5px;
396 | right: 0px;
397 | }
398 |
--------------------------------------------------------------------------------