diff --git a/CourseEditorOperations.php b/CourseEditorOperations.php index 61fef31..10cf8cb 100644 --- a/CourseEditorOperations.php +++ b/CourseEditorOperations.php @@ -1,288 +1,314 @@ type) { case 'fromTopic': self::createNewCourseFromTopic($operation->params); break; case 'fromDepartment': self::createNewCourseFromDepartment($operation->params); break; } //FIXME Must be a serius return object and error handling return "ok"; } public static function manageCourseMetadataOp($operationRequested){ $operation = json_decode($operationRequested); $params = $operation->params; $title = $params[0]; $topic = $params[1]; $description = $params[2]; $externalReferences = $params[3]; $isImported = $params[4]; $originalAuthors = $params[5]; $isReviewed = $params[6]; $reviewedOn = $params[7]; $pageTitle = MWNamespace::getCanonicalName(NS_COURSEMETADATA) . ':' . $title; $metadata = "
" . $topic . "
\r\n"; if($description !== '' && $description !== null){ $metadata .= "
" . $description . "
\r\n"; } if($externalReferences !== '' && $externalReferences !== null){ $metadata .= "
" . $externalReferences . "
\r\n"; } if($isImported !== false || $isReviewed !== false){ $metadata .= "
" . true . "
\r\n"; if($isImported !== false){ $metadata .= "
" . $isImported . "
\r\n"; $metadata .= "
" . $originalAuthors . "
\r\n"; } if($isReviewed !== false){ $metadata .= "
" . $isReviewed . "
\r\n"; $metadata .= "
" . $reviewedOn . "
\r\n"; } } $resultCreateMetadataPage = CourseEditorUtils::editWrapper($pageTitle, $metadata , null, null); CourseEditorUtils::setSingleOperationSuccess($operation, $resultCreateMetadataPage); return json_encode($operation); //FIXME Return an object with results in order to display error to the user } private function createBasicCourseMetadata($topic, $title, $description){ $topic = ($topic === null ? $title : $topic); $pageTitle = MWNamespace::getCanonicalName(NS_COURSEMETADATA) . ':' . $title; $metadata = "
" . $topic . "
\r\n"; if($description !== '' && $description !== null){ $metadata .= "
" . $description . "
\r\n"; } $resultCreateMetadataPage = CourseEditorUtils::editWrapper($pageTitle, $metadata , null, null); //FIXME Return an object with results in order to display error to the user } private function createNewCourseFromDepartment($params){ $department = $params[0]; $title = $params[1]; $description = $params[2]; $namespace = $params[3]; if($department != null && $title != null && $namespace != null){ $compareResult = strcmp($namespace, 'NS_COURSE'); $namespaceCostant = ($compareResult == 0 ? NS_COURSE : NS_USER); $pageTitle = MWNamespace::getCanonicalName($namespaceCostant) . ':'; if($namespaceCostant == NS_USER){ self::createPrivateCourse($pageTitle, null, $title, $description); }else{ self::createPublicCourseFromDepartment($pageTitle, $department, $title, $description); } } } private function createNewCourseFromTopic($params){ $topic = $params[0]; $title = $params[1]; $description = $params[2]; $namespace = $params[3]; if($topic != null && $title != null && $namespace != null){ $compareResult = strcmp($namespace, 'NS_COURSE'); $namespaceCostant = ($compareResult === 0 ? NS_COURSE : NS_USER); $pageTitle = MWNamespace::getCanonicalName($namespaceCostant) . ':'; if($namespaceCostant == NS_USER){ self::createPrivateCourse($pageTitle, $topic, $title, $description); }else{ self::createPublicCourseFromTopic($pageTitle, $topic, $title, $description); } } } private function createPrivateCourse($pageTitle, $topic, $title, $description){ $context = CourseEditorUtils::getRequestContext(); $user = $context->getUser(); $userPage = $pageTitle . $user->getName(); $titleWithUser = $user->getName() . '/' . $title; $pageTitle = $userPage . "/" . $title; $resultCreateCourse = CourseEditorUtils::editWrapper($pageTitle, "{{CCourse|}}", null, null); $resultCreateMetadataPage = self::createBasicCourseMetadata($topic, $titleWithUser, $description); $textToPrepend = "{{Course|" . $title . "|" . $user->getName() . "}}"; $resultPrependToUserPage = CourseEditorUtils::editWrapper($userPage, null, $textToPrepend, null); //FIXME Return an object with results in order to display error to the user } private function createPublicCourseFromTopic($pageTitle, $topic, $title, $description){ $pageTitle .= $title; $resultCreateCourse = CourseEditorUtils::editWrapper($pageTitle, "{{CCourse|}}", null, null); $topicCourses = CourseEditorUtils::getTopicCourses($topic); $text = $topicCourses . "{{Course|" . $title . "}}}}"; $resultCreateMetadataPage = self::createBasicCourseMetadata($topic, $title, $description); $resultAppendToTopic = CourseEditorUtils::editWrapper($topic, $text, null, null); //FIXME Return an object with results in order to display error to the user } private function createPublicCourseFromDepartment($pageTitle, $department, $title, $description){ $pageTitle .= $title; $resultCreateCourse = CourseEditorUtils::editWrapper($pageTitle, "{{CCourse|}}", null, null); $text = "{{Topic|" . "{{Course|" . $title . "}}}}"; $listElementText = "\r\n* [[" . $title . "]]"; $resultCreateMetadataPage = self::createBasicCourseMetadata(null, $title, $description); $resultAppendToTopic = CourseEditorUtils::editWrapper($title, $text, null, null); $resultAppendToDepartment = CourseEditorUtils::editWrapper($department, null, null, $listElementText); //FIXME Return an object with results in order to display error to the user } public static function applyCourseOp($courseName, $operation){ $value = json_decode($operation); switch ($value->action) { - case 'rename': + case 'rename-move-task': $sectionName = $value->elementName; $newSectionName = $value->newElementName; - $chapters = CourseEditorUtils::getChapters($courseName . '/' .$sectionName); + /*$chapters = CourseEditorUtils::getChapters($courseName . '/' .$sectionName); $newSectionText = ""; foreach ($chapters as $chapter) { $newSectionText .= "* [[" . $courseName . "/" . $newSectionName . "/" . $chapter ."|". $chapter ."]]\r\n"; - } + }*/ $pageTitle = $courseName . "/" . $sectionName; $newPageTitle = $courseName . '/' . $newSectionName; - $resultMove = CourseEditorUtils::moveWrapper($pageTitle, $newPageTitle); - $resultEdit = CourseEditorUtils::editWrapper($newPageTitle, $newSectionText, null, null); - $apiResult = array($resultMove, $resultEdit); - CourseEditorUtils::setComposedOperationSuccess($value, $apiResult); + $apiResult = CourseEditorUtils::moveWrapper($pageTitle, $newPageTitle); + //$resultEdit = CourseEditorUtils::editWrapper($newPageTitle, $newSectionText, null, null); + //$apiResult = array($resultMove, $resultEdit); + CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; - case 'delete': + case 'rename-update-task': + $sectionName = $value->elementName; + $newSectionName = $value->newElementName; + $chapters = CourseEditorUtils::getChapters($courseName . '/' .$newSectionName); + $newSectionText = ""; + foreach ($chapters as $chapter) { + $newSectionText .= "* [[" . $courseName . "/" . $newSectionName . "/" . $chapter ."|". $chapter ."]]\r\n"; + } + //$pageTitle = $courseName . "/" . $sectionName; + $newPageTitle = $courseName . '/' . $newSectionName; + //$apiResult = CourseEditorUtils::moveWrapper($pageTitle, $newPageTitle); + $apiResult = CourseEditorUtils::editWrapper($newPageTitle, $newSectionText, null, null); + //$apiResult = array($resultMove, $resultEdit); + CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); + break; + case 'delete-chapters-task': $user = CourseEditorUtils::getRequestContext()->getUser(); $sectionName = $value->elementName; $chapters = CourseEditorUtils::getChapters($courseName . '/' . $sectionName); $title = Title::newFromText( $courseName . '/' . $sectionName, $defaultNamespace=NS_MAIN ); + $pageTitle = $courseName . '/' . $sectionName; if(!$title->userCan('delete', $user, 'secure')){ - $resultChapters = true; - $pageTitle = $courseName . '/' . $sectionName; $prependText = "\r\n{{DeleteMe}}"; - $resultSection = CourseEditorUtils::editWrapper($pageTitle, null, $prependText, null); + //$resultSection = CourseEditorUtils::editWrapper($pageTitle, null, $prependText, null); foreach ($chapters as $chapter) { $pageTitle = $courseName . '/' . $sectionName . '/' . $chapter; $prependText = "\r\n{{DeleteMe}}"; - $resultChapters = CourseEditorUtils::editWrapper($pageTitle, null, $prependText, null); + $apiResult = CourseEditorUtils::editWrapper($pageTitle, null, $prependText, null); } }else { - $resultChapters = true; foreach ($chapters as $chapter) { $pageTitle = $courseName . '/' . $sectionName . '/' . $chapter; $resultChapters = CourseEditorUtils::deleteWrapper($pageTitle); } - $pageTitle = $courseName . '/' . $sectionName; - $resultSection = CourseEditorUtils::deleteWrapper($pageTitle); + //$resultSection = CourseEditorUtils::deleteWrapper($pageTitle); } - $apiResult = array($resultSection, $resultChapters); - CourseEditorUtils::setComposedOperationSuccess($value, $apiResult); + //$apiResult = array($resultSection, $resultChapters); + CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); + break; + case 'delete-section-task': + $user = CourseEditorUtils::getRequestContext()->getUser(); + $sectionName = $value->elementName; + $title = Title::newFromText( $courseName . '/' . $sectionName, $defaultNamespace=NS_MAIN ); + $pageTitle = $courseName . '/' . $sectionName; + if(!$title->userCan('delete', $user, 'secure')){ + $prependText = "\r\n{{DeleteMe}}"; + $apiResult = CourseEditorUtils::editWrapper($pageTitle, null, $prependText, null); + }else { + $apiResult = CourseEditorUtils::deleteWrapper($pageTitle); + } + CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; case 'add': $sectionName = $value->elementName; $pageTitle = $courseName . '/' . $sectionName; $text = ""; $apiResult = CourseEditorUtils::editWrapper($pageTitle, $text, null, null); CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; case 'update': $newCourseText = "{{CCourse|\r\n"; $newSectionsArray = json_decode($value->elementsList); foreach ($newSectionsArray as $section) { $newCourseText .= "{{SSection|" . $section ."}}\r\n"; } $newCourseText .= "}}"; $categories = CourseEditorUtils::getCategories($courseName); if(sizeof($categories) > 0){ foreach ($categories as $category) { $newCourseText .= "\r\n[[" . $category['title'] . "]]"; } } $apiResult = CourseEditorUtils::editWrapper($courseName, $newCourseText, null, null); CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; case 'update-collection': $apiResult = CourseEditorUtils::updateCollection($courseName); CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; - case 'fix-link': + /*case 'fix-link': $targetPage = $value->elementName; $linkToReplace = $value->linkToReplace; list($course, $section, $chapter) = explode('/', $linkToReplace); $replacement = $course . '/' . $value->replacement . '/' . $chapter; $title = Title::newFromText($targetPage); $page = WikiPage::factory( $title ); $content = $page->getContent( Revision::RAW ); $text = ContentHandler::getContentText( $content ); - str_replace($linkToReplace, $replacement, $text); - $apiResult = CourseEditorUtils::editWrapper($targetPage, $text, null, null); + $newText = str_replace(str_replace(' ', '_', $linkToReplace), $replacement, $text); + $apiResult = CourseEditorUtils::editWrapper($targetPage, $newText, null, null); CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); - break; + $value->text = $newText; + break;*/ } return json_encode($value); } public static function applySectionOp($sectionName, $operation){ $context = CourseEditorUtils::getRequestContext(); $value = json_decode($operation); switch ($value->action) { case 'rename': $chapterName = $value->elementName; $newChapterName = $value->newElementName; $from = $sectionName . '/' . $chapterName; $to = $sectionName . '/' . $newChapterName; $apiResult = CourseEditorUtils::moveWrapper($from, $to); CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; case 'delete': $user = $context->getUser(); $chapterName = $value->elementName; $title = Title::newFromText($sectionName . '/' . $chapterName, $defaultNamespace=NS_MAIN); if(!$title->userCan('delete', $user, 'secure')){ $pageTitle = $sectionName . '/' . $chapterName; $prependText = "\r\n{{DeleteMe}}"; $apiResult = CourseEditorUtils::editWrapper($pageTitle, null, $prependText, null); }else { $pageTitle = $sectionName . '/' . $chapterName; $apiResult = CourseEditorUtils::deleteWrapper($pageTitle); } CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; case 'add': $chapterName = $value->elementName; $pageTitle = $sectionName . '/' . $chapterName; $text = ""; $apiResult = CourseEditorUtils::editWrapper($pageTitle, $text, null, null); CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; case 'update': $newSectionText = ""; $newChaptersArray = json_decode($value->elementsList); foreach ($newChaptersArray as $chapter) { $newSectionText .= "* [[" . $sectionName . "/" . $chapter ."|". $chapter ."]]\r\n"; } $apiResult = CourseEditorUtils::editWrapper($sectionName, $newSectionText); CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; case 'purge': $explodedString = explode("/", $sectionName); $pageToBePurged = (sizeof($explodedString) > 2 ? $explodedString[0] . "/" . $explodedString[1] : $explodedString[0]); $apiResult = CourseEditorUtils::purgeWrapper($pageToBePurged); CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; case 'update-collection': $explodedString = explode("/", $sectionName); $courseName = (sizeof($explodedString) > 2 ? $explodedString[0] . "/" . $explodedString[1] : $explodedString[0]); $apiResult = CourseEditorUtils::updateCollection($courseName); CourseEditorUtils::setSingleOperationSuccess($value, $apiResult); break; } return json_encode($value); } } diff --git a/CourseEditorUtils.php b/CourseEditorUtils.php index 39442bf..d90127a 100644 --- a/CourseEditorUtils.php +++ b/CourseEditorUtils.php @@ -1,286 +1,290 @@ getNamespace(); if(MWNamespace::equals($namespaceIndex, NS_USER)){ self::updateUserCollection($courseName); }else { $pageTitle = "Project:" . wfMessage('courseeditor-collection-book-category') ."/" . $name; $collectionText = "{{" . wfMessage('courseeditor-collection-savedbook-template') . " \n| setting-papersize = a4 \n| setting-toc = auto \n| setting-columns = 1 \n| setting-footer = yes\n}}\n"; $collectionText .= "== " . str_replace('_', ' ', $name) . " ==\r\n"; $sections = self::getSections($courseName); foreach ($sections as $section) { $chapters = self::getChapters($courseName . '/' .$section); $collectionText .= ";" . $section . "\r\n"; foreach ($chapters as $chapter) { $collectionText .= ":[[" . $courseName . "/" . $section . "/" . $chapter . "]]\r\n"; } } $categoryName = wfMessage('courseeditor-collection-book-category'); if ( !$categoryName->isDisabled() ) { $catTitle = Title::makeTitle( NS_CATEGORY, $categoryName ); if ( !is_null( $catTitle ) ) { $collectionText .= "\n[[" . $catTitle->getPrefixedText() ."|" . $name . "]]"; } } $editResult = self::editWrapper($pageTitle, $collectionText, null, null); return $editResult; } } private function updateUserCollection($courseName){ list($namespaceAndUser, $title) = explode('/', $courseName, 2); $pageTitle = $namespaceAndUser . "/" . wfMessage('courseeditor-collection-book-category') . "/" . $title; $collectionText = "{{" . wfMessage('courseeditor-collection-savedbook-template') . " \n| setting-papersize = a4 \n| setting-toc = auto \n| setting-columns = 1 \n| setting-footer = yes\n}}\n"; $collectionText .= "== " . str_replace('_', ' ', $title). " ==\r\n"; $sections = self::getSections($courseName); foreach ($sections as $section) { $chapters = self::getChapters($courseName . '/' .$section); $collectionText .= ";" . $section . "\r\n"; foreach ($chapters as $chapter) { $collectionText .= ":[[" . $courseName . "/" . $section . "/" . $chapter . "]]\r\n"; } } $categoryName = wfMessage('courseeditor-collection-book-category'); if ( !$categoryName->isDisabled() ) { $catTitle = Title::makeTitle( NS_CATEGORY, $categoryName ); if ( !is_null( $catTitle ) ) { $collectionText .= "\n[[" . $catTitle->getPrefixedText() ."|" . $title. "]]"; } } $editResult = self::editWrapper($pageTitle, $collectionText, null, null); return $editResult; } public static function getMetadata($courseName){ $title = Title::newFromText($courseName, $defaultNamespace=NS_COURSEMETADATA ); $page = WikiPage::factory( $title ); $content = $page->getContent( Revision::RAW ); $text = ContentHandler::getContentText( $content ); if($text === ''){ return null; } $regex = "/
(.*?)
/s"; preg_match_all($regex, $text, $matches, PREG_PATTERN_ORDER); $metadataResult = array(); $metadataKeys = $matches[1]; $metadataValues = $matches[2]; for ($i=0; $i < sizeof($metadataKeys); $i++) { $metadataResult[$metadataKeys[$i]] = $metadataValues[$i]; } return $metadataResult; } public static function getTopicCourses($topic){ $title = Title::newFromText($topic, $defaultNamespace=NS_MAIN ); $page = WikiPage::factory( $title ); $content = $page->getContent( Revision::RAW ); $text = ContentHandler::getContentText( $content ); $textNoNewLines = trim(preg_replace('/\n+/', '', $text)); $regex = "/({{Topic|.+)}}.*$/"; preg_match_all($regex, $textNoNewLines, $matches, PREG_PATTERN_ORDER); return $matches[1][0]; } /** * This method is a workaround (read it HACK) to check the API results. * MediaWiki ApiResult object is not "standard" but if an error/exception * occurs the result variable is a string. */ public static function setSingleOperationSuccess(&$operation, $result){ $isSuccess = true; if (is_string($result)) { $isSuccess = false; } $operation->success = $isSuccess; } /** * This method is a workaround (read it HACK) to check the API results. * MediaWiki ApiResult object is not "standard" but if an error/exception * occurs the result variable is a string. */ public static function setComposedOperationSuccess(&$operation, $result){ $isSuccess = true; if (is_string($result[0]) || is_string($result[1])) { $isSuccess = false; } $operation->success = $isSuccess; } public static function getRequestContext(){ if(self::$requestContext == null) { $context = new RequestContext(); self::$requestContext = $context; } return self::$requestContext; } public static function getCategories($courseName){ try { $api = new ApiMain( new DerivativeRequest( self::getRequestContext()->getRequest(), array( 'action' => 'query', 'titles' => $courseName, 'prop' => 'categories' ) ), true ); $api->execute(); $results = $api->getResult()->getResultData(null, array('Strip' => 'all')); $page = reset($results['query']['pages']); return $page['categories']; } catch(UsageException $e){ return $e->getMessage(); } } public static function getChapters($sectionName){ $title = Title::newFromText($sectionName, $defaultNamespace=NS_MAIN ); $page = WikiPage::factory( $title ); $content = $page->getContent( Revision::RAW ); $text = ContentHandler::getContentText( $content ); $regex = "/\*\s*\[{2}([^|]*)\|?([^\]]*)\]{2}\s*/"; preg_match_all($regex, $text, $matches, PREG_PATTERN_ORDER); return $matches[2]; } public static function getSections($courseName){ $title = Title::newFromText( $courseName, $defaultNamespace=NS_MAIN ); $page = WikiPage::factory( $title ); $content = $page->getContent( Revision::RAW ); $text = ContentHandler::getContentText( $content ); $regex = "/\{{2}SSection\|(.*)\}{2}/"; preg_match_all($regex, $text, $matches, PREG_PATTERN_ORDER); return $matches[1]; } public static function deleteWrapper($title){ $context = self::getRequestContext(); try { $user = $context->getUser(); $token = $user->getEditToken(); $api = new ApiMain( new DerivativeRequest( $context->getRequest(), array( 'action' => 'delete', 'title' => $title, 'token' => $token ), true ), true ); $api->execute(); return $api->getResult()->getResultData(null, array('Strip' => 'all')); } catch(UsageException $e){ return $e->getMessage(); } } public static function purgeWrapper($titles){ $context = self::getRequestContext(); try { $api = new ApiMain( new DerivativeRequest( $context->getRequest(), array( 'action' => 'purge', 'titles' => $titles, 'forcerecursivelinkupdate' => true ), true ), true ); $api->execute(); return $api->getResult()->getResultData(null, array('Strip' => 'all')); } catch(UsageException $e){ return $e->getMessage(); } } public static function editWrapper($title, $text, $textToPrepend, $textToAppend){ $context = self::getRequestContext(); try { $user = $context->getUser(); $token = $user->getEditToken(); //$token = $this->getCsrfToken(); $api = new ApiMain( new DerivativeRequest( $context->getRequest(), array( 'action' => 'edit', 'title' => $title, 'text' => $text, // automatically override text 'prependtext' => $textToPrepend, // automatically override text 'appendtext' => $textToAppend, 'notminor' => true, 'token' => $token ), true ), true ); $api->execute(); return $api->getResult()->getResultData(null, array('Strip' => 'all')); } catch(UsageException $e){ return $e->getMessage(); } } public static function moveWrapper($from, $to){ $context = self::getRequestContext(); try { $user = $context->getUser(); $token = $user->getEditToken(); $api = new ApiMain( new DerivativeRequest( $context->getRequest(), array( 'action' => 'move', 'from' => $from, 'to' => $to, - 'noredirect' => true, + //'noredirect' => true, 'movetalk' => true, 'movesubpages' => true, 'token' => $token ), true ), true ); $api->execute(); return $api->getResult()->getResultData(null, array('Strip' => 'all')); } catch(UsageException $e){ return $e->getMessage(); } } + + public static function getBacklinksWrapper($title){ + + } } diff --git a/modules/commonFunctions.js b/modules/commonFunctions.js index 699a3c0..c5a44c2 100644 --- a/modules/commonFunctions.js +++ b/modules/commonFunctions.js @@ -1,420 +1,465 @@ /* Create a gloabal windowManager to open dialogs and append it to the body*/ var windowManager = new OO.ui.WindowManager(); $('body').append( windowManager.$element ); /******** UTIL METHODS ********/ -var createMicroOperations = function(operation, callback){ +var createMicroOperations = function(operation){ switch (operation.action) { case 'rename': - createMicroRenameOperations(operation, function(results){ - callback(results); - }); + return createRenameMicroOperations(operation); + break; + case 'delete': + return createDeleteMicroOperations(operation); + break; default: - //no default + return createDefaultMicroOperations(operation); } }; +var createDefaultMicroOperations = function(operation){ + var microOps = []; + microOps.push(operation); + return microOps; +} + +var createRenameMicroOperations = function(operation) { + var microOps = []; + microOps.push({ + action: 'rename-move-task', + elementName: operation.elementName, + newElementName: operation.newElementName + }); + microOps.push({ + action: 'rename-update-task', + elementName: operation.elementName, + newElementName: operation.newElementName + }); + return microOps; +}; + +var createDeleteMicroOperations = function(operation) { + var microOps = []; + microOps.push({ + action: 'delete-chapters-task', + elementName: operation.elementName + }); + microOps.push({ + action: 'delete-section-task', + elementName: operation.elementName + }); + return microOps; +}; + +/* +var createMicroDefaultOperations = function(operation, callback) { + var microOps = []; + microOps.push(operation); + callback(microOps); +}; + var createMicroRenameOperations = function(operation, callback) { var title = new mw.Title($('#courseName').text()); var microOps = []; - //Move the page and all its subpages - microOps.push(operation); getSubpages(title, operation, function(subpages){ for (var i = 0; i < subpages.query.allpages.length; i++) { var page = subpages.query.allpages[i]; + //The better HACK ever: not return the callback until the for dosn't + // completed if(i === subpages.query.allpages.length - 1) { getMicroOpsFromBacklinks(page, operation, microOps, function(microOps) { + //Move the page and all its subpages + microOps.push(operation); callback(microOps); }); } else { getMicroOpsFromBacklinks(page, operation, microOps, function(){}); } } }); }; var getMicroOpsFromBacklinks = function(page, operation, microOps, returnMicroOps){ var api = new mw.Api(); api.get( { action: 'query', list: 'backlinks', bltitle: page.title, } ).done( function ( data) { if(data.query.backlinks.length > 0){ var backlinks = data.query.backlinks; backlinks.shift(); //the course with transcluded pages for (var i = 0; i < backlinks.length; i++) { microOps.push({ action: 'fix-link', elementName: backlinks[i].title, linkToReplace: page.title, replacement: operation.newElementName }); } - returnMicroOps(microOps); } + returnMicroOps(microOps); }); }; var getSubpages = function (title, operation, returnSubpages){ var api = new mw.Api(); api.get( { action: 'query', list: 'allpages', apprefix: title.getMain() + "/" + operation.elementName, apnamespace: title.getNamespaceId() } ).done( function ( data) { returnSubpages(data); }); -}; +};*/ /** * Init handlers * @param {DraggableGroupWidget} [draggableWidget] * @param {TextInputWidget} [textInputWidget] * @param {Array} [editStack] */ var initHandlers = function(draggableWidget, textInputWidget, editStack){ $('.deleteElementIcon').click(function(){ deleteElement(draggableWidget, $(this).parent().text(), editStack); }); $('.editElementIcon').click(function(){ editElement(draggableWidget, $(this).parent().text(), editStack); }); $('.oo-ui-inputWidget-input').attr('id', 'addElement'); $('#addElement').blur(function(){ $('#alert').hide(); addElement(draggableWidget, textInputWidget.getValue(), editStack); textInputWidget.setValue(''); }); $('#addElement').keypress(function(keypressed) { if(keypressed.which === 13) { addElement(draggableWidget, textInputWidget.getValue(), editStack); textInputWidget.setValue(''); } }); }; /** * Find the index of a deleted element in the editStack * @param {String} [elementName] * @param {Array} [editStack] */ var findIndexOfDeletedElement = function(editStack, elementName) { for (var i = 0; i < editStack.length; i++) { if (editStack[i]['action'] === 'delete' && editStack[i]['elementName'] === elementName) { return i; } } return null; }; /** * Find the index of already added or renamed element in the editStack * @param {String} [elementName] * @param {DraggableWidget} [draggableWidget] * @return boolean */ var elementExist = function(draggableWidget, elementName) { var items = draggableWidget.getItems(); for (var item in items) { if (items[item].data === elementName) { return true; } } return false; }; /** * Create a drag item, its handlers on edit and remove icons and append it to * to the draggableWidget. * @param {DraggableGroupWidget} [draggableWidget] * @param {String} [elementName] * @param {Array} [editStack] */ var createDragItem = function(draggableWidget, elementName, editStack){ //Create item and icons var dragItem = new DraggableHandledItemWidget( { data: elementName, icon: 'menu', label: elementName } ); var iconDelete = $(""); var iconEdit = $(""); //Append icons and add the item to draggableWidget dragItem.$label.append(iconDelete, iconEdit); draggableWidget.addItems([dragItem]); //Create handlers $(iconDelete).click(function(){ deleteElement(draggableWidget, $(this).parent().text(), editStack); }); $(iconEdit).click(function(){ editElement(draggableWidget, $(this).parent().text(), editStack); }); }; /** * Create a button list group item, its handler on undo and append it to * to the RecycleBin list group. * @param {DraggableGroupWidget} [draggableWidget] * @param {String} [elementName] * @param {Array} [editStack] */ var createRecycleBinItem = function(draggableWidget, elementName, editStack){ //Create item and icon var liButton = $('
  •   ' + elementName +'
  • '); var undoDeleteIcon = $(''); //Append icon and add the item to the list liButton.prepend(undoDeleteIcon); $('.list-group').append(liButton); //Create handler $(undoDeleteIcon).click(function(){ var elementToRestore = $(this).parent().attr('id'); restoreElement(draggableWidget, elementToRestore, editStack); }); } /******** HELPER METHODS ********/ var dequeue = function(queueName){ $(document).dequeue(queueName); }; /** * Delete a element from the draggableWidget and add a item to the * RecycleBin list. * @param {DraggableGroupWidget} [draggableWidget] * @param {String} [elementName] * @param {Array} [editStack] */ var deleteElement = function(draggableWidget, elementName, editStack){ var elementToRemove = draggableWidget.getItemFromData(elementName); draggableWidget.removeItems([elementToRemove]); editStack.push({ action: 'delete', elementName: elementName }); createRecycleBinItem(draggableWidget, elementName, editStack); }; /** * Restore a element from the RecycleBin and remove its deletion * from the editStack * @param {DraggableGroupWidget} [draggableWidget] * @param {String} [elementName] * @param {Array} [editStack] */ var restoreElement = function(draggableWidget, elementName, editStack){ createDragItem(draggableWidget, elementName, editStack); editStack.splice(editStack.indexOf({action: 'delete', element: elementName})); $('li[id="' + elementName + '"]').remove(); }; /** * Add a element to the draggableWidget automatically if its name isn't * in the RecycleBin list, otherwise open a MessageDialog and ask to the user * if he/she prefer to restore the element or create a new one. * @param {DraggableGroupWidget} [draggableWidget] * @param {String} [elementName] * @param {Array} [editStack] */ var addElement = function(draggableWidget, elementName, editStack){ if($.trim(elementName).length !== 0){ if(findIndexOfDeletedElement(editStack, elementName) !== null){ var messageDialog = new OO.ui.MessageDialog(); windowManager.addWindows( [ messageDialog ] ); windowManager.openWindow( messageDialog, { title: OO.ui.deferMsg('courseeditor-message-dialog-title'), message: OO.ui.deferMsg('courseeditor-message-dialog-message'), actions: [ { action: 'reject', label: OO.ui.deferMsg('courseeditor-message-dialog-cancel'), flags: 'safe' }, { action: 'restore', label: OO.ui.deferMsg('courseeditor-message-dialog-restore') }, { action: 'confirm', label: OO.ui.deferMsg('courseeditor-message-dialog-create-new'), flags: [ 'primary', 'constructive' ] } ] } ).then( function ( opened ) { opened.then( function ( closing, data ) { if ( data && data.action === 'restore' ) { restoreElement(draggableWidget, elementName, editStack); } else if(data && data.action === 'confirm') { createDragItem(draggableWidget, elementName, editStack); editStack.push({ action: 'add', elementName: elementName }); } } ); } ); }else if (elementExist(draggableWidget, elementName)) { $('#alert').show(); }else { createDragItem(draggableWidget, elementName, editStack); editStack.push({ action: 'add', elementName: elementName }); } } }; /** * Rename a element * @param {DraggableGroupWidget} [draggableWidget] * @param {String} [elementName] * @param {Array} [editStack] */ var editElement = function(draggableWidget, elementName, editStack){ var dialog = new EditDialog(draggableWidget, elementName, editStack); windowManager.addWindows( [ dialog ] ); windowManager.openWindow( dialog ); }; /******** OO.UI OBJECTS ********/ //var progressBar = new OO.ui.ProgressBarWidget(); function ProgressDialog( config ) { ProgressDialog.parent.call( this, config ); }; OO.inheritClass( ProgressDialog, OO.ui.Dialog ); ProgressDialog.static.escapable = false; ProgressDialog.prototype.initialize = function () { ProgressDialog.parent.prototype.initialize.call( this ); this.progressBar = new OO.ui.ProgressBarWidget({ progress: 0 }); this.currentOp = new OO.ui.LabelWidget( { label: '' } ); this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } ); this.content.$element.append(this.progressBar.$element, this.currentOp.$element); this.$body.append( this.content.$element ); }; ProgressDialog.prototype.getBodyHeight = function () { return this.content.$element.outerHeight( true ); }; ProgressDialog.prototype.updateProgress = function(unitaryIncrement){ var currentProgress = this.progressBar.getProgress(); this.progressBar.setProgress(currentProgress + unitaryIncrement); }; ProgressDialog.prototype.setCurrentOp = function(operation){ var labelToSet = OO.ui.msg('courseeditor-operation-action-' + operation.action); if(operation.elementName){ labelToSet += " " + operation.elementName; } this.currentOp.setLabel(labelToSet); }; /****** Draggable Widget ******/ /** * Draggable group widget containing drag/drop items * * @param {Object} [config] Configuration options */ function DraggableGroupWidget( config ) { // Configuration initialization config = config || {}; // Parent constructor DraggableGroupWidget.parent.call( this, config ); // Mixin constructors OO.ui.mixin.DraggableGroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) ); } /* Setup */ OO.inheritClass( DraggableGroupWidget, OO.ui.Widget ); OO.mixinClass( DraggableGroupWidget, OO.ui.mixin.DraggableGroupElement ); /** * Drag/drop items with custom handle * * @param {Object} [config] Configuration options */ function DraggableHandledItemWidget( config ) { // Configuration initialization config = config || {}; // Parent constructor DraggableHandledItemWidget.parent.call( this, config ); // Mixin constructors OO.ui.mixin.DraggableElement.call( this, $.extend( { $handle: this.$icon }, config ) ); } /* Setup */ OO.inheritClass( DraggableHandledItemWidget, OO.ui.DecoratedOptionWidget ); OO.mixinClass( DraggableHandledItemWidget, OO.ui.mixin.DraggableElement ); /****** Edit Dialog ******/ /* Create a dialog */ function EditDialog(draggableWidget, elementName, editStack, config ) { EditDialog.parent.call( this, config ); this.draggableWidget = draggableWidget; this.elementName = elementName; this.editStack = editStack; this.textInputWidget = new OO.ui.TextInputWidget($.extend( { validate: 'non-empty' }, config ) ); this.textInputWidget.setValue(elementName); } /* Inheritance */ OO.inheritClass( EditDialog, OO.ui.ProcessDialog ); /* Static Properties */ EditDialog.static.title = OO.ui.deferMsg( 'courseeditor-edit-dialog' ); EditDialog.static.actions = [ { action: 'save', label: OO.ui.deferMsg( 'courseeditor-rename' ), flags: 'primary' }, { label: OO.ui.deferMsg( 'courseeditor-cancel' ), flags: 'safe' } ]; /* Initialize the dialog elements */ EditDialog.prototype.initialize = function () { EditDialog.parent.prototype.initialize.apply( this, arguments ); this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } ); this.content.$element.append(this.textInputWidget.$element ); this.$body.append( this.content.$element ); }; /* Define actions */ EditDialog.prototype.getActionProcess = function ( action ) { var dialog = this; if ( action === 'save' ) { return new OO.ui.Process( function () { var newElementName = dialog.textInputWidget.getValue(); var items = dialog.draggableWidget.getItems(); items.filter(function(element) { if(element.data === dialog.elementName){ element.setData(newElementName); element.setLabel(newElementName); var iconDelete = $(""); var iconEdit = $(""); element.$label.append(iconDelete, iconEdit); $(iconDelete).click(function(){ deleteElement(dialog.draggableWidget, $(this).parent().text(), dialog.editStack); }); $(iconEdit).click(function(){ editElement(dialog.draggableWidget, $(this).parent().text(), dialog.editStack); }); dialog.editStack.push({ action: 'rename', elementName: dialog.elementName, newElementName: newElementName }) } }); dialog.close( { action: action } ); } ); } return EditDialog.parent.prototype.getActionProcess.call( this, action ); }; diff --git a/modules/courseEditor.js b/modules/courseEditor.js index 1fd62ee..946f513 100644 --- a/modules/courseEditor.js +++ b/modules/courseEditor.js @@ -1,101 +1,138 @@ $(function () { var dragElements = []; //Add all existing sections to the dragSections array $.each(sections, function(key, value){ var dragItem = new DraggableHandledItemWidget( { data: value, icon: 'menu', label: value } ); dragItem.$label.append("", ""); dragElements.push(dragItem); }); //Create a draggableWidget with the items in the dragSections array var draggableWidget = new DraggableGroupWidget( { items: dragElements } ); var fieldDrag = new OO.ui.FieldLayout(draggableWidget); //Create a textInputWidget for new sections var textInputWidget = new OO.ui.TextInputWidget( { placeholder: OO.ui.deferMsg( 'courseeditor-add-new-section' ) } ); var fieldInput = new OO.ui.FieldLayout( textInputWidget); //Append all created elements to DOM $('#sectionsList').append(fieldDrag.$element, fieldInput.$element); initHandlers(draggableWidget, textInputWidget, editStack); $('#saveCourseButton').click(function(){ var newSections = []; $.each(draggableWidget.getItems(), function(key, value){ newSections.push(value.data); }); - /*editStack.push({ + editStack.push({ action: 'update', elementsList: JSON.stringify(newSections) }); editStack.push({ action: 'update-collection' - });*/ + }); var progressDialog = new ProgressDialog( { size: 'medium' } ); var unitaryIncrement = 100/editStack.length; windowManager.addWindows( [ progressDialog ] ); windowManager.openWindow( progressDialog ); - var createTask = function(operation){ - return function(next){ - doTask(operation, next); - } - }; - - var doTask = function(operation, next){ - progressDialog.setCurrentOp(operation); + var doTask = function(microOp, next){ + progressDialog.setCurrentOp(microOp); $.getJSON( mw.util.wikiScript(), { action: 'ajax', rs: 'CourseEditorOperations::applyCourseOp', - rsargs: [$('#courseName').text(), JSON.stringify(operation)] + rsargs: [$('#courseName').text(), JSON.stringify(microOp)] }, function ( data ) { + console.log(data); if (data.success !== true) { var alert = '
    '; $('#saveDiv').after(alert); $('#alert').html(OO.ui.deferMsg('courseeditor-error-operation')); $('#alert').append(OO.ui.deferMsg('courseeditor-operation-action-' + data.action)); if(data.elementName){ var localizedMsg = " " + data.elementName + OO.ui.msg('courseeditor-error-operation-fail'); $('#alert').append(localizedMsg); }else { $('#alert').append(OO.ui.deferMsg('courseeditor-error-operation-fail')); } windowManager.closeWindow(progressDialog); $(document).clearQueue('tasks'); }else { progressDialog.updateProgress(unitaryIncrement); next(); } }); }; - while( editStack.length > 0 ) { - var operation = editStack.shift(); + var createTask = function(microOp){ + return function(next){ + doTask(microOp, next); + } + }; + + /*function prepareCreateMicroOperations(operation) { + var dfd=$.Deferred(); createMicroOperations(operation, function(microOps){ for (var i = 0; i < microOps.length; i++) { - //console.log(microOps[i]); + console.log(microOps[i]); $(document).queue('tasks', createTask(microOps[i])); } + dfd.resolve(); }); + return dfd.promise(); + } + + var promises = []; + + while (editStack.length > 0) { + var operation = editStack.shift(); + promises.push(prepareCreateMicroOperations(operation)); + } + + $.when.apply($, promises).done(function(){ + $(document).queue('tasks', createTask({ + action: 'update', + elementsList: JSON.stringify(newSections) + })); + + $(document).queue('tasks', createTask({ + action: 'update-collection' + })); + + $(document).queue('tasks', function(){ + windowManager.closeWindow(progressDialog); + //window.location.assign('/' + $('#courseName').text()); + }); + + dequeue('tasks'); + });*/ + + while( editStack.length > 0) { + var operation = editStack.shift(); + var microOps = createMicroOperations(operation); + for (var i = 0; i < microOps.length; i++) { + $(document).queue('tasks', createTask(microOps[i])); + } }; + $(document).queue('tasks', function(){ windowManager.closeWindow(progressDialog); window.location.assign('/' + $('#courseName').text()); }); dequeue('tasks'); }); }) diff --git a/modules/sectionEditor.js b/modules/sectionEditor.js index 448c675..4f6385a 100644 --- a/modules/sectionEditor.js +++ b/modules/sectionEditor.js @@ -1,101 +1,97 @@ $(function () { var dragElements = []; //Add all existing chapters to the dragElements array $.each(chapters, function(key, value){ var dragItem = new DraggableHandledItemWidget( { data: value, icon: 'menu', label: value } ); dragItem.$label.append("", ""); dragElements.push(dragItem); }); //Create a draggableWidget with the items in the dragElements array var draggableWidget = new DraggableGroupWidget( { items: dragElements } ); var fieldDrag = new OO.ui.FieldLayout(draggableWidget); //Create a textInputWidget for new chapters var textInputWidget = new OO.ui.TextInputWidget( { placeholder: OO.ui.deferMsg( 'courseeditor-add-new-chapter' ) } ); var fieldInput = new OO.ui.FieldLayout( textInputWidget); //Append all created elements to DOM $('#chaptersList').append(fieldDrag.$element, fieldInput.$element); //Init Handlers initHandlers(draggableWidget, textInputWidget, editStack); $('#saveSectionButton').click(function(){ var newChapters = []; $.each(draggableWidget.getItems(), function(key, value){ newChapters.push(value.data); }); editStack.push({ action: 'update', elementsList: JSON.stringify(newChapters) }); editStack.push({ action: 'purge' }); editStack.push({ action: 'update-collection' }); var progressDialog = new ProgressDialog( { size: 'medium' } ); - var unitaryIncrement = 100/editStack.length; - windowManager.addWindows( [ progressDialog ] ); windowManager.openWindow( progressDialog ); var createTask = function(operation){ return function(next){ doTask(operation, next); } }; var doTask = function(operation, next){ - progressDialog.setCurrentOp(operation); $.getJSON( mw.util.wikiScript(), { action: 'ajax', rs: 'CourseEditorOperations::applySectionOp', rsargs: [$('#sectionName').text(), JSON.stringify(operation)] }, function ( data ) { if (data.success !== true) { var alert = '
    '; $('#saveDiv').after(alert); $('#alert').html(OO.ui.msg('courseeditor-error-operation')); - $('#alert').append(OO.ui.msg('courseeditor-operation-action-' + data.action)); + $('#alert').append(OO.ui.msg('courseeditor-error-operation-action-' + data.action)); if(data.elementName){ var localizedMsg = " " + data.elementName + OO.ui.msg('courseeditor-error-operation-fail'); $('#alert').append(localizedMsg); }else { $('#alert').append(OO.ui.msg('courseeditor-error-operation-fail')); } windowManager.closeWindow(progressDialog); $(document).clearQueue('tasks'); }else{ - progressDialog.updateProgress(unitaryIncrement); next(); } }); }; while( editStack.length > 0 ) { var operation = editStack.shift(); $(document).queue('tasks', createTask(operation)); }; $(document).queue('tasks', function(){ windowManager.closeWindow(progressDialog); window.location.assign('/' + $('#sectionName').text()); }); dequeue('tasks') }); })