Index: trunk/extensions/SemanticMediaWiki/includes/SMW_QueryProcessor.php |
— | — | @@ -537,19 +537,22 @@ |
538 | 538 | */ |
539 | 539 | protected function getLinkDescription(&$setNS, &$label) { |
540 | 540 | // This method is called when we encountered an opening '[['. The following |
541 | | - // block could be a Category-statement, fixed object, property statements, |
| 541 | + // block could be a Category-statement, fixed object, property statements, |
542 | 542 | // or according print statements. |
543 | | - $chunk = $this->readChunk(); |
544 | | - if ($chunk == $this->m_categoryprefix) { // category statement |
| 543 | + $chunk = $this->readChunk('',true,false); // NOTE: untrimmed, initial " " escapes prop. chains |
| 544 | + if (smwfNormalTitleText($chunk) == $this->m_categoryprefix) { // category statement |
545 | 545 | return $this->getCategoryDescription($setNS, $label); |
546 | 546 | } else { // fixed subject, namespace restriction, property query, or subquery |
547 | 547 | $sep = $this->readChunk('',false); //do not consume hit, "look ahead" |
548 | | - if ($sep == '::') { // relation statement |
549 | | - return $this->getPropertyDescription($chunk, $setNS, $label); |
550 | | - } elseif ($sep == ':=') { // attribute statement |
551 | | - return $this->getPropertyDescription($chunk, $setNS, $label); |
| 548 | + if ( ($sep == '::') || ($sep == ':=') ) { |
| 549 | + if ($chunk{0} !=':') { // property statement |
| 550 | + return $this->getPropertyDescription($chunk, $setNS, $label); |
| 551 | + } else { // escaped article description, read part after :: to get full contents |
| 552 | + $chunk .= $this->readChunk('\[\[|\]\]|\|\||\|'); |
| 553 | + return $this->getArticleDescription(trim($chunk), $setNS, $label); |
| 554 | + } |
552 | 555 | } else { // Fixed article/namespace restriction. $sep should be ]] or || |
553 | | - return $this->getArticleDescription($chunk, $setNS, $label); |
| 556 | + return $this->getArticleDescription(trim($chunk), $setNS, $label); |
554 | 557 | } |
555 | 558 | } |
556 | 559 | } |
— | — | @@ -611,16 +614,31 @@ |
612 | 615 | protected function getPropertyDescription($propertyname, &$setNS, &$label) { |
613 | 616 | global $smwgIP; |
614 | 617 | include_once($smwgIP . '/includes/SMW_DataValueFactory.php'); |
615 | | - $this->readChunk(); // consume seperator ":=" |
616 | | - $property = Title::newFromText($propertyname, SMW_NS_PROPERTY); |
617 | | - if ($property === NULL) { |
618 | | - $this->m_errors[] .= wfMsgForContent('smw_badtitle', htmlspecialchars($propertyname)); |
619 | | - return NULL; ///TODO: read some more chunks and try to finish [[ ]] |
| 618 | + $this->readChunk(); // consume separator ":=" or "::" |
| 619 | + // first process property chain syntax (e.g. "property1.property2::value"): |
| 620 | + if ($propertyname{0} == ' ') { // escape |
| 621 | + $propertynames = array($propertyname); |
| 622 | + } else { |
| 623 | + $propertynames = explode('.', $propertyname); |
620 | 624 | } |
| 625 | + $properties = array(); |
| 626 | + $typeid = '_wpg'; |
| 627 | + foreach ($propertynames as $name) { |
| 628 | + if ($typeid != '_wpg') { // non-final property in chain was no wikipage: not allowed |
| 629 | + $this->m_errors[] .= wfMsgForContent('smw_valuesubquery', end($name)); |
| 630 | + return NULL; ///TODO: read some more chunks and try to finish [[ ]] |
| 631 | + } |
| 632 | + $property = Title::newFromText($name, SMW_NS_PROPERTY); |
| 633 | + $typeid = SMWDataValueFactory::getPropertyObjectTypeID($property); |
| 634 | + if ($property === NULL) { // illegal title |
| 635 | + $this->m_errors[] .= wfMsgForContent('smw_badtitle', htmlspecialchars($name)); |
| 636 | + return NULL; ///TODO: read some more chunks and try to finish [[ ]] |
| 637 | + } |
| 638 | + $properties[] = $property; |
| 639 | + } ///NOTE: after iteration, $property and $typeid correspond to last value |
621 | 640 | |
622 | 641 | $innerdesc = NULL; |
623 | 642 | $continue = true; |
624 | | - $typeid = SMWDataValueFactory::getPropertyObjectTypeID($property); |
625 | 643 | while ($continue) { |
626 | 644 | $chunk = $this->readChunk(); |
627 | 645 | switch ($chunk) { |
— | — | @@ -639,7 +657,7 @@ |
640 | 658 | $sublabel = ''; |
641 | 659 | $innerdesc = $this->addDescription($innerdesc, $this->getSubqueryDescription($setsubNS, $sublabel), false); |
642 | 660 | } else { // no subqueries allowed for non-pages |
643 | | - $this->m_errors[] = wfMsgForContent('smw_valuesubquery', $propertyname); |
| 661 | + $this->m_errors[] = wfMsgForContent('smw_valuesubquery', end($propertynames)); |
644 | 662 | $innerdesc = $this->addDescription($innerdesc, new SMWThingDescription(), false); |
645 | 663 | } |
646 | 664 | $chunk = $this->readChunk(); |
— | — | @@ -649,6 +667,7 @@ |
650 | 668 | $open = 1; |
651 | 669 | $value = $chunk; |
652 | 670 | $continue2 = true; |
| 671 | + // read value with inner [[, ]], || |
653 | 672 | while ( ($open > 0) && ($continue2) ) { |
654 | 673 | $chunk = $this->readChunk('\[\[|\]\]|\|\||\|'); |
655 | 674 | switch ($chunk) { |
— | — | @@ -663,14 +682,14 @@ |
664 | 683 | $open = 0; |
665 | 684 | } |
666 | 685 | break; |
667 | | - case '': // this is not good ... TODO:report error |
| 686 | + case '': ///TODO: report error; this is not good right now |
668 | 687 | $continue2 = false; |
669 | 688 | break; |
670 | 689 | } |
671 | 690 | if ($open != 0) { |
672 | 691 | $value .= $chunk; |
673 | 692 | } |
674 | | - } // note that at this point, we normally already read one more chunk behind the value |
| 693 | + } ///NOTE: at this point, we normally already read one more chunk behind the value |
675 | 694 | |
676 | 695 | if ($typeid == '__nry') { // nary value |
677 | 696 | $dv = SMWDataValueFactory::newPropertyObjectValue($property); |
— | — | @@ -744,8 +763,11 @@ |
745 | 764 | } |
746 | 765 | $this->m_errors[] = wfMsgForContent('smw_propvalueproblem', $property->getText()); |
747 | 766 | } |
748 | | - $result = new SMWSomeProperty($property,$innerdesc); |
749 | | - |
| 767 | + $properties = array_reverse($properties); |
| 768 | + foreach ($properties as $property) { |
| 769 | + $innerdesc = new SMWSomeProperty($property,$innerdesc); |
| 770 | + } |
| 771 | + $result = $innerdesc; |
750 | 772 | return $this->finishLinkDescription($chunk, false, $result, $setNS, $label); |
751 | 773 | } |
752 | 774 | |
— | — | @@ -795,7 +817,7 @@ |
796 | 818 | |
797 | 819 | /** |
798 | 820 | * Parse an article description (the part of an inline query that |
799 | | - * is in between "[[" and the closing "]]" if it is not specifying |
| 821 | + * is in between "[[" and the closing "]]" assuming it is not specifying |
800 | 822 | * a category or property) and create a suitable description. |
801 | 823 | * The first chunk behind the "[[" has already been read and is |
802 | 824 | * passed as a parameter. |
— | — | @@ -829,9 +851,9 @@ |
830 | 852 | } |
831 | 853 | } |
832 | 854 | |
833 | | - $chunk = $this->readChunk(); |
| 855 | + $chunk = $this->readChunk('\[\[|\]\]|\|\||\|'); |
834 | 856 | if ($chunk == '||') { |
835 | | - $chunk = $this->readChunk(); |
| 857 | + $chunk = $this->readChunk('\[\[|\]\]|\|\||\|'); |
836 | 858 | $continue = true; |
837 | 859 | } else { |
838 | 860 | $continue = false; |
— | — | @@ -885,7 +907,7 @@ |
886 | 908 | * (such as [[, ]], <q>, ...). If the string starts with such a delimiter, |
887 | 909 | * this delimiter is returned. Otherwise the first string in front of such a |
888 | 910 | * delimiter is returned. |
889 | | - * Trailing and initial spaces are always ignored and chunks |
| 911 | + * Trailing and initial spaces are ignored if $trim is true, and chunks |
890 | 912 | * consisting only of spaces are not returned. |
891 | 913 | * If there is no more qurey string left to process, the empty string is |
892 | 914 | * returned (and in no other case). |
— | — | @@ -896,27 +918,27 @@ |
897 | 919 | * $consume specifies whether the returned chunk should be removed from the |
898 | 920 | * query string. |
899 | 921 | */ |
900 | | - protected function readChunk($stoppattern = '', $consume=true) { |
| 922 | + protected function readChunk($stoppattern = '', $consume=true, $trim=true) { |
901 | 923 | if ($stoppattern == '') { |
902 | 924 | $stoppattern = '\[\[|\]\]|::|:=|<q>|<\/q>|^' . $this->m_categoryprefix . '|\|\||\|'; |
903 | 925 | } |
904 | | - $chunks = preg_split('/[\s]*(' . $stoppattern . ')[\s]*/u', $this->m_curstring, 2, PREG_SPLIT_DELIM_CAPTURE); |
| 926 | + $chunks = preg_split('/(' . $stoppattern . ')/u', $this->m_curstring, 2, PREG_SPLIT_DELIM_CAPTURE); |
905 | 927 | if (count($chunks) == 1) { // no matches anymore, strip spaces and finish |
906 | 928 | if ($consume) { |
907 | 929 | $this->m_curstring = ''; |
908 | 930 | } |
909 | | - return trim($chunks[0]); |
910 | | - } elseif (count($chunks) == 3) { // this chould generally happen if count is not 1 |
| 931 | + return $trim?trim($chunks[0]):$chunks[0]; |
| 932 | + } elseif (count($chunks) == 3) { // this should generally happen if count is not 1 |
911 | 933 | if ($chunks[0] == '') { // string started with delimiter |
912 | 934 | if ($consume) { |
913 | 935 | $this->m_curstring = $chunks[2]; |
914 | 936 | } |
915 | | - return $chunks[1]; // spaces stripped already |
| 937 | + return $trim?trim($chunks[1]):$chunks[1]; |
916 | 938 | } else { |
917 | 939 | if ($consume) { |
918 | 940 | $this->m_curstring = $chunks[1] . $chunks[2]; |
919 | 941 | } |
920 | | - return $chunks[0]; // spaces stripped already |
| 942 | + return $trim?trim($chunks[0]):$chunks[0]; |
921 | 943 | } |
922 | 944 | } else { return false; } //should never happen |
923 | 945 | } |