| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package WebService::Cmis::Repository; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | =head1 NAME | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | WebService::Cmis::Repository - Representation of a cmis repository | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | After fetching a L object, fetching the | 
| 10 |  |  |  |  |  |  | repository is the next thing to do in most cases using L. | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | my $repo = WebService::Cmis::getClient->getRepository('repositoryId'); | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | =cut | 
| 15 |  |  |  |  |  |  |  | 
| 16 | 1 |  |  | 1 |  | 21083 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 48 |  | 
| 17 | 1 |  |  | 1 |  | 7 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 38 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 1 |  |  | 1 |  | 5 | use WebService::Cmis qw(:namespaces :collections :utils :relations :contenttypes); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 419 |  | 
| 20 | 1 |  |  | 1 |  | 509 | use XML::LibXML qw(:libxml); | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | use Error qw(:try); | 
| 22 |  |  |  |  |  |  | use WebService::Cmis::NotImplementedException (); | 
| 23 |  |  |  |  |  |  | use WebService::Cmis::NotSupportedException (); | 
| 24 |  |  |  |  |  |  | use WebService::Cmis::ClientException (); | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | our $CMIS_XPATH_REPOSITORYINFO = new XML::LibXML::XPathExpression('./*[local-name() = "repositoryInfo" and namespace-uri() = "'.CMISRA_NS.'"]/*[local-name() != "capabilities" and local-name() != "aclCapability" and namespace-uri() = "'.CMIS_NS.'"]'); | 
| 27 |  |  |  |  |  |  | our $CMIS_XPATH_CAPABILITIES = new XML::LibXML::XPathExpression('./*[local-name() = "repositoryInfo" and namespace-uri() = "'.CMISRA_NS.'"]/*[local-name() = "capabilities" and namespace-uri() = "'.CMIS_NS.'"]/*'); | 
| 28 |  |  |  |  |  |  | our $CMIS_XPATH_SUPPORTED_PERMISSIONS = new XML::LibXML::XPathExpression('./*[local-name() = "repositoryInfo" and namespace-uri() = "'.CMISRA_NS.'"]/*[local-name() = "aclCapability" and namespace-uri() = "'.CMIS_NS.'"]/*[local-name() = "supportedPermissions" and namespace-uri() = "'.CMIS_NS.'"]'); | 
| 29 |  |  |  |  |  |  | our $CMIS_XPATH_PROPAGATION = new XML::LibXML::XPathExpression('./*[local-name() = "repositoryInfo" and namespace-uri() = "'.CMISRA_NS.'"]/*[local-name() = "aclCapability" and namespace-uri() = "'.CMIS_NS.'"]/*[local-name() = "propagation" and namespace-uri() = "'.CMIS_NS.'"]'); | 
| 30 |  |  |  |  |  |  | our $CMIS_XPATH_PERMISSION_DEFINITION = new XML::LibXML::XPathExpression('./*[local-name() = "repositoryInfo" and namespace-uri() = "'.CMISRA_NS.'"]/*[local-name() = "aclCapability" and namespace-uri() = "'.CMIS_NS.'"]/*[local-name() = "permissions" and namespace-uri() = "'.CMIS_NS.'"]'); | 
| 31 |  |  |  |  |  |  | our $CMIS_XPATH_PERMISSION_MAP = new XML::LibXML::XPathExpression('./*[local-name() = "repositoryInfo" and namespace-uri() = "'.CMISRA_NS.'"]/*[local-name() = "aclCapability" and namespace-uri() = "'.CMIS_NS.'"]/*[local-name() = "mapping" and namespace-uri() = "'.CMIS_NS.'"]'); | 
| 32 |  |  |  |  |  |  | our $CMIS_XPATH_URITEMPLATE = new XML::LibXML::XPathExpression('./*[local-name() = "uritemplate" and namespace-uri() = "'.CMISRA_NS.'"]'); | 
| 33 |  |  |  |  |  |  | our $CMIS_XPATH_COLLECTION = new XML::LibXML::XPathExpression('./*[local-name() = "collection" and namespace-uri()="'.APP_NS.'" and @href]'); | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | =head1 METHODS | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | =over 4 | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | =item new($client, $xmlDoc) | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | Create a new repository object using the given $client and loading | 
| 42 |  |  |  |  |  |  | the information stored in the $xmlDoc. | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | =cut | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | sub new { | 
| 47 |  |  |  |  |  |  | my ($class, $client, $xmlDoc) = @_; | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | my $this = bless({ | 
| 50 |  |  |  |  |  |  | client => $client, | 
| 51 |  |  |  |  |  |  | xmlDoc => $xmlDoc, | 
| 52 |  |  |  |  |  |  | }, $class); | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | $this->_initData; | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | return $this; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | =item getClient() -> L | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | returns the the client object used to communicate with the repository. | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | =cut | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | sub getClient { | 
| 66 |  |  |  |  |  |  | return $_[0]->{client}; | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | # internal function to reset cached data | 
| 71 |  |  |  |  |  |  | sub _initData { | 
| 72 |  |  |  |  |  |  | my $this = shift; | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | $this->{repositoryInfo} = undef; | 
| 75 |  |  |  |  |  |  | $this->{capabilities} = undef; | 
| 76 |  |  |  |  |  |  | $this->{uriTemplates} = undef; | 
| 77 |  |  |  |  |  |  | $this->{permDefs} = undef; | 
| 78 |  |  |  |  |  |  | $this->{permMap} = undef; | 
| 79 |  |  |  |  |  |  | $this->{permissions} = undef; | 
| 80 |  |  |  |  |  |  | $this->{propagation} = undef; | 
| 81 |  |  |  |  |  |  | $this->{uriTempaltes} = undef; | 
| 82 |  |  |  |  |  |  | $this->{collectionLink} = undef; | 
| 83 |  |  |  |  |  |  | $this->{typeDefs} = undef; | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | sub DESTROY { | 
| 87 |  |  |  |  |  |  | my $this = shift; | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | #print STDERR "called Repository::DESTROY\n"; | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | undef $this->{repositoryInfo}; | 
| 92 |  |  |  |  |  |  | undef $this->{capabilities}; | 
| 93 |  |  |  |  |  |  | undef $this->{uriTemplates}; | 
| 94 |  |  |  |  |  |  | undef $this->{permDefs}; | 
| 95 |  |  |  |  |  |  | undef $this->{permMap}; | 
| 96 |  |  |  |  |  |  | undef $this->{permissions}; | 
| 97 |  |  |  |  |  |  | undef $this->{propagation}; | 
| 98 |  |  |  |  |  |  | undef $this->{uriTempaltes}; | 
| 99 |  |  |  |  |  |  | undef $this->{collectionLink}; | 
| 100 |  |  |  |  |  |  | undef $this->{typeDefs}; | 
| 101 |  |  |  |  |  |  | undef $this->{xmlDoc}; | 
| 102 |  |  |  |  |  |  | undef $this->{client}; | 
| 103 |  |  |  |  |  |  | undef $this->{fileMage}; | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | =item toString() | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | return a string representation of this repository | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | =cut | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | sub toString { | 
| 113 |  |  |  |  |  |  | my $this = shift; | 
| 114 |  |  |  |  |  |  | return $this->getRepositoryId; | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =item reload() | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | This method will re-fetch the repository's XML data from the CMIS | 
| 120 |  |  |  |  |  |  | repository. | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | =cut | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | sub reload { | 
| 125 |  |  |  |  |  |  | my $this = shift; | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | $this->{xmlDoc} = $this->{client}->get; | 
| 128 |  |  |  |  |  |  | $this->_initData; | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | #internal helper to make sure the xmlDoc is loaded | 
| 132 |  |  |  |  |  |  | sub _xmlDoc { | 
| 133 |  |  |  |  |  |  | $_[0]->reload unless defined $_[0]->{xmlDoc}; | 
| 134 |  |  |  |  |  |  | return $_[0]->{xmlDoc}; | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | =item getRepositoryId() | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | returns this repository's ID | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | =cut | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | sub getRepositoryId { | 
| 144 |  |  |  |  |  |  | return $_[0]->getRepositoryInfo->{repositoryId}; | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | =item getRepositoryName() | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | returns this repository's name | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | =cut | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | sub getRepositoryName { | 
| 154 |  |  |  |  |  |  | return $_[0]->getRepositoryInfo->{repositoryName}; | 
| 155 |  |  |  |  |  |  | } | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | =item getRepositoryInfo() -> %info | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | returns this repository's info record | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | See CMIS specification document 2.2.2.2 getRepositoryInfo | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | =cut | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | sub getRepositoryInfo { | 
| 166 |  |  |  |  |  |  | my $this = shift; | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | unless (defined $this->{repositoryInfo}) { | 
| 169 |  |  |  |  |  |  | $this->{repositoryInfo}{$_->localname} = $_->string_value | 
| 170 |  |  |  |  |  |  | foreach $this->_xmlDoc->findnodes($CMIS_XPATH_REPOSITORYINFO); | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | return $this->{repositoryInfo}; | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | =item getCapabilities() -> %caps | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | returns this repository's capabilities | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | =cut | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | sub getCapabilities { | 
| 183 |  |  |  |  |  |  | my $this = shift; | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | unless (defined $this->{capabilities}) { | 
| 186 |  |  |  |  |  |  | require WebService::Cmis::Property::Boolean; | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | $this->{capabilities} = {}; | 
| 189 |  |  |  |  |  |  | foreach my $node ($this->_xmlDoc->findnodes($CMIS_XPATH_CAPABILITIES)) { | 
| 190 |  |  |  |  |  |  | my $key = $node->localname; | 
| 191 |  |  |  |  |  |  | $key =~ s/^capability//; | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | my $val = $node->string_value; | 
| 194 |  |  |  |  |  |  | $val = WebService::Cmis::Property::Boolean->parse($val) if $val =~ /^(true|false)$/; | 
| 195 |  |  |  |  |  |  | $this->{capabilities}{$key} = $val; | 
| 196 |  |  |  |  |  |  | } | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | return $this->{capabilities}; | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =item getSupportedPermissions() -> $permissions | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | returns this repository's supported permissions. | 
| 205 |  |  |  |  |  |  | values are: | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | basic: indicates that the CMIS Basic permissions are supported | 
| 208 |  |  |  |  |  |  | repository: indicates that repository specific permissions are supported | 
| 209 |  |  |  |  |  |  | both: indicates that both CMIS basic permissions and repository specific permissions are supported | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | =cut | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | sub getSupportedPermissions { | 
| 214 |  |  |  |  |  |  | my $this = shift; | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | unless ($this->getCapabilities()->{'ACL'}) { | 
| 217 |  |  |  |  |  |  | throw WebService::Cmis::NotSupportedException("This repository does not support ACLs"); | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | unless (defined $this->{permissions}) { | 
| 221 |  |  |  |  |  |  | $this->{permissions} = $this->_xmlDoc->findvalue($CMIS_XPATH_SUPPORTED_PERMISSIONS); | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | return $this->{permissions}; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | =item getPropagation() -> $string | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | returns the value of the cmis:propagation element. Valid values are: | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | objectonly: indicates that the repository is able to apply ACEs | 
| 232 |  |  |  |  |  |  | without changing the ACLs of other objects | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | propagate: indicates that the repository is able to apply ACEs to a | 
| 235 |  |  |  |  |  |  | given object and propagate this change to all inheriting objects | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | =cut | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | sub getPropagation { | 
| 240 |  |  |  |  |  |  | my $this = shift; | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | unless ($this->getCapabilities()->{'ACL'}) { | 
| 243 |  |  |  |  |  |  | throw WebService::Cmis::NotSupportedException("This repository does not support ACLs"); | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | unless (defined $this->{propagation}) { | 
| 247 |  |  |  |  |  |  | $this->{propagation} = $this->_xmlDoc->findvalue($CMIS_XPATH_PROPAGATION); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | return $this->{propagation}; | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | =item getPermissionDefinitions() -> %permDefs | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | Returns a hash of permission definitions for this repository. The key is the | 
| 256 |  |  |  |  |  |  | permission string or technical name of the permission and the value is the | 
| 257 |  |  |  |  |  |  | permission description. | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | =cut | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | sub getPermissionDefinitions { | 
| 262 |  |  |  |  |  |  | my $this = shift; | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | unless ($this->getCapabilities()->{'ACL'}) { | 
| 265 |  |  |  |  |  |  | throw WebService::Cmis::NotSupportedException("This repository does not support ACLs"); | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | unless (defined $this->{permDefs}) { | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | foreach my $node ($this->_xmlDoc->findnodes($CMIS_XPATH_PERMISSION_DEFINITION)) { | 
| 271 |  |  |  |  |  |  | my ($permNode) = $node->getElementsByTagNameNS(CMIS_NS, 'permission'); # these two getElementsByTagNameNS are ok | 
| 272 |  |  |  |  |  |  | my ($descNode) = $node->getElementsByTagNameNS(CMIS_NS, 'description'); | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | if (defined $permNode && defined $descNode) { | 
| 275 |  |  |  |  |  |  | # alfresco has got a detailed sub node | 
| 276 |  |  |  |  |  |  | $this->{permDefs}{$permNode->string_value} = $descNode->string_value; | 
| 277 |  |  |  |  |  |  | } else { | 
| 278 |  |  |  |  |  |  | # TODO: nuxeo looks differernt down here | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | return $this->{permDefs}; | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | =item getPermissionMap() -> %permMap | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | returns a hash representing the permission mapping table where | 
| 289 |  |  |  |  |  |  | each key is a permission key string and each value is a list of one or | 
| 290 |  |  |  |  |  |  | more permissions the principal must have to perform the operation. | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | =cut | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | sub getPermissionMap { | 
| 295 |  |  |  |  |  |  | my $this = shift; | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | unless ($this->getCapabilities()->{'ACL'}) { | 
| 298 |  |  |  |  |  |  | throw WebService::Cmis::NotSupportedException("This repository does not support ACLs"); | 
| 299 |  |  |  |  |  |  | } | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | unless (defined $this->{permMap}) { | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | foreach my $node ($this->_xmlDoc->findnodes($CMIS_XPATH_PERMISSION_MAP)) { | 
| 304 |  |  |  |  |  |  | my @permList = (); | 
| 305 |  |  |  |  |  |  | my ($keyNode) = $node->getElementsByTagNameNS(CMIS_NS, 'key'); # these two getElementsByTagNameNS are ok | 
| 306 |  |  |  |  |  |  | foreach my $permNode ($node->getElementsByTagNameNS(CMIS_NS, 'permission')) { | 
| 307 |  |  |  |  |  |  | push @permList, $permNode->string_value; | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  | $this->{permMap}{$keyNode->string_value} = \@permList; | 
| 310 |  |  |  |  |  |  | } | 
| 311 |  |  |  |  |  |  | } | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | return $this->{permMap} | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | =item getUriTemplates() -> %templates | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | returns a hash of URI templates the repository service knows about. | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | =cut | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | sub getUriTemplates { | 
| 323 |  |  |  |  |  |  | my $this = shift; | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | unless (defined $this->{uriTemplates}) { | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | foreach my $node ($this->_xmlDoc->findnodes($CMIS_XPATH_URITEMPLATE)) { | 
| 328 |  |  |  |  |  |  | my $template; | 
| 329 |  |  |  |  |  |  | my $type; | 
| 330 |  |  |  |  |  |  | my $mediaType; | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | foreach my $subNode ($node->childNodes) { | 
| 333 |  |  |  |  |  |  | next if $subNode->nodeType != XML_ELEMENT_NODE; | 
| 334 |  |  |  |  |  |  | my $localName = $subNode->localname; | 
| 335 |  |  |  |  |  |  | if ($localName eq 'template') { | 
| 336 |  |  |  |  |  |  | $template = $subNode->string_value; | 
| 337 |  |  |  |  |  |  | } elsif ($localName eq 'type') { | 
| 338 |  |  |  |  |  |  | $type = $subNode->string_value; | 
| 339 |  |  |  |  |  |  | } elsif ($localName eq 'mediatype') { | 
| 340 |  |  |  |  |  |  | $mediaType = $subNode->string_value; | 
| 341 |  |  |  |  |  |  | } | 
| 342 |  |  |  |  |  |  | last if defined $template && defined $type && defined $mediaType; | 
| 343 |  |  |  |  |  |  | } | 
| 344 |  |  |  |  |  |  | $this->{uriTemplates}{$type} = { | 
| 345 |  |  |  |  |  |  | template => $template, | 
| 346 |  |  |  |  |  |  | type => $type, | 
| 347 |  |  |  |  |  |  | mediatype => $mediaType, | 
| 348 |  |  |  |  |  |  | }; | 
| 349 |  |  |  |  |  |  | } | 
| 350 |  |  |  |  |  |  | } | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | return $this->{uriTemplates}; | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | =item getUriTemplate($type) -> $template | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | returns an uri template for the given type | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | =cut | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | sub getUriTemplate { | 
| 362 |  |  |  |  |  |  | my ($this, $type) = @_; | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | return $this->getUriTemplates()->{$type}->{template}; | 
| 365 |  |  |  |  |  |  | } | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | =item getRootFolder() -> $folder | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | returns the root folder of the repository | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | =cut | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | sub getRootFolder { | 
| 374 |  |  |  |  |  |  | my $this = shift; | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | my $id = $this->getRepositoryInfo->{'rootFolderId'}; | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | return $this->getFolder($id) if $id; | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | # some repos don't advertise the root folder | 
| 381 |  |  |  |  |  |  | return $this->getObjectByPath("/"); | 
| 382 |  |  |  |  |  |  | } | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | =item getFolder($id) -> $foldeer | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | returns the a folder object of the given id | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | =cut | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | sub getFolder { | 
| 391 |  |  |  |  |  |  | my ($this, $id) = @_; | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | require WebService::Cmis::Folder; | 
| 394 |  |  |  |  |  |  | return new WebService::Cmis::Folder(repository=>$this, id=>$id); | 
| 395 |  |  |  |  |  |  | } | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | =item getCollection($collectionType, %args) -> $atomFeed | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | returns a AtomFeed of objects returned for the specified collection. | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | If the query collection is requested, an exception will be throwd. | 
| 402 |  |  |  |  |  |  | That collection isn't meant to be retrieved. | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | =cut | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | sub getCollection { | 
| 407 |  |  |  |  |  |  | my $this = shift; | 
| 408 |  |  |  |  |  |  | my $collectionType = shift; | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | if ($collectionType eq QUERY_COLL) { | 
| 411 |  |  |  |  |  |  | throw Error::Simple("query collection not supported"); # SMELL: use a custom exception | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | my $link = $this->getCollectionLink($collectionType); | 
| 415 |  |  |  |  |  |  | my $result = $this->{client}->get($link, @_); | 
| 416 |  |  |  |  |  |  | #_writeCmisDebug("result=".$result->toString); | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | # return the result set | 
| 419 |  |  |  |  |  |  | if ($collectionType eq TYPES_COLL) { | 
| 420 |  |  |  |  |  |  | require WebService::Cmis::AtomFeed::ObjectTypes; | 
| 421 |  |  |  |  |  |  | return new WebService::Cmis::AtomFeed::ObjectTypes(repository=>$this, xmlDoc=>$result); | 
| 422 |  |  |  |  |  |  | } else { | 
| 423 |  |  |  |  |  |  | require WebService::Cmis::AtomFeed::Objects; | 
| 424 |  |  |  |  |  |  | return new WebService::Cmis::AtomFeed::Objects(repository=>$this, xmlDoc=>$result); | 
| 425 |  |  |  |  |  |  | } | 
| 426 |  |  |  |  |  |  | } | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | =item getTypeDefinition($typeId) -> $objectType | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | returns an ObjectType object for the specified object type id. | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | See CMIS specification document 2.2.2.5 getTypeDefinition | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | folderType = repo.getTypeDefinition('cmis:folder') | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | =cut | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | sub getTypeDefinition { | 
| 439 |  |  |  |  |  |  | my ($this, $id) = @_; | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | require WebService::Cmis::ObjectType; | 
| 442 |  |  |  |  |  |  | my $objectType = new WebService::Cmis::ObjectType(repository=>$this, id=>$id); | 
| 443 |  |  |  |  |  |  | $objectType->reload; | 
| 444 |  |  |  |  |  |  | return $objectType; | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | =item getCollectionLink($collectionType) -> $href | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | returns the link HREF from the specified collectionType | 
| 450 |  |  |  |  |  |  | (CHECKED_OUT_COLL, for example). | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | =cut | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | sub getCollectionLink { | 
| 455 |  |  |  |  |  |  | my ($this, $collectionType) = @_; | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | unless ($this->{collectionLink}) { | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | foreach my $node ($this->_xmlDoc->findnodes($CMIS_XPATH_COLLECTION)) { | 
| 460 |  |  |  |  |  |  | my $href = $node->attributes->getNamedItem('href')->value; | 
| 461 |  |  |  |  |  |  | foreach my $subNode ($node->childNodes) { | 
| 462 |  |  |  |  |  |  | next unless $subNode->nodeType == XML_ELEMENT_NODE && $subNode->localname eq 'collectionType'; | 
| 463 |  |  |  |  |  |  | $this->{collectionLink}{$subNode->string_value} = $href; | 
| 464 |  |  |  |  |  |  | } | 
| 465 |  |  |  |  |  |  | } | 
| 466 |  |  |  |  |  |  | #_writeCmisDebug("collection link for $collectionType: $this->{collectionLink}{$collectionType}"); | 
| 467 |  |  |  |  |  |  | } | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | return $this->{collectionLink}{$collectionType}; | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | =item getLink($relation) -> $href | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | returns the HREF attribute of an Atom link element for the | 
| 475 |  |  |  |  |  |  | specified rel. | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | =cut | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | sub getLink { | 
| 480 |  |  |  |  |  |  | my ($this, $relation) = @_; | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | my $href = $this->_xmlDoc->find('./*[local-name() = "link" and namespace-uri() = "'.ATOM_NS.'" and @rel="'.$relation.'"]/@href'); | 
| 483 |  |  |  |  |  |  | return "".$href if $href; | 
| 484 |  |  |  |  |  |  | return; | 
| 485 |  |  |  |  |  |  | } | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | =item getObjectByPath($path, %params) -> $cmisObj | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | returns an object given the path to the object. | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | my $doc = $repo->getObjectByPath("/User homes/jeff/sample.pdf"); | 
| 492 |  |  |  |  |  |  | my $title = $doc->getTitle(); | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | These optional arguments are supported: | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | =over 4 | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | =item filter: See section 2.2.1.2.1 Properties. | 
| 499 |  |  |  |  |  |  |  | 
| 500 |  |  |  |  |  |  | =item includeAllowableActions: See section 2.2.1.2.6 Allowable Actions. | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | =item includeRelationships: See section 2.2.1.2.2 Relationships. | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | =item renditionFilter: See section 2.2.1.2.4 Renditions. | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | =item includePolicyIds: See section 2.2.1.2.2 Relationships. | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | =item includeACL: See section 2.2.1.2.5 ACLs. | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | =back | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | See CMIS specification document 2.2.4.9 getObjectByPath | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | =cut | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | sub getObjectByPath { | 
| 517 |  |  |  |  |  |  | my $this = shift; | 
| 518 |  |  |  |  |  |  | my $path = shift; | 
| 519 |  |  |  |  |  |  | my %params = @_; | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | # get the uritemplate | 
| 522 |  |  |  |  |  |  | my $template = $this->getUriTemplate('objectbypath'); | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | require WebService::Cmis::Property::Boolean; | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | $path ||= '/'; | 
| 527 |  |  |  |  |  |  | $template =~ s/{path}/delete $params{path}||$path/ge; | 
| 528 |  |  |  |  |  |  | $template =~ s/{filter}/delete $params{filter}||''/ge; | 
| 529 |  |  |  |  |  |  | $template =~ s/{includeAllowableActions}/WebService::Cmis::Property::Boolean->unparse(delete $params{includeAllowableActions}||'false')/ge; | 
| 530 |  |  |  |  |  |  | $template =~ s/{includePolicyIds}/WebService::Cmis::Property::Boolean->unparse(delete $params{includePolicyIds}||'false')/ge; | 
| 531 |  |  |  |  |  |  | $template =~ s/{includeRelationships}/WebService::Cmis::Property::Boolean->unparse(delete $params{includeRelationships}||'')/ge; | 
| 532 |  |  |  |  |  |  | $template =~ s/{includeACL}/WebService::Cmis::Property::Boolean->unparse(delete $params{includeACL}||'false')/ge; | 
| 533 |  |  |  |  |  |  | $template =~ s/{renditionFilter}/delete $params{renditionFilter}||''/ge; | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | #print STDERR "template=$template\n"; | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | # do a GET against the URL | 
| 538 |  |  |  |  |  |  | my $result; | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | try { | 
| 541 |  |  |  |  |  |  | $result = $this->{client}->get($template, @_); | 
| 542 |  |  |  |  |  |  | } catch WebService::Cmis::ClientException with { | 
| 543 |  |  |  |  |  |  | # ignore | 
| 544 |  |  |  |  |  |  | }; | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | return unless $result; | 
| 547 |  |  |  |  |  |  |  | 
| 548 |  |  |  |  |  |  | require WebService::Cmis::Object; | 
| 549 |  |  |  |  |  |  | return new WebService::Cmis::Object(repository=>$this, xmlDoc=>$result, extra_params=>\%params); | 
| 550 |  |  |  |  |  |  | } | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | =item getObject($id, %params) -> $cmisObj | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | returns an object given the specified object ID. | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | See CMIS specification document 2.2.4.7 getObject | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | =cut | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | sub getObject { | 
| 561 |  |  |  |  |  |  | my $this = shift; | 
| 562 |  |  |  |  |  |  | my $id = shift; | 
| 563 |  |  |  |  |  |  | my %params = @_; | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | require WebService::Cmis::Object; | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | my $obj; | 
| 568 |  |  |  |  |  |  | try { | 
| 569 |  |  |  |  |  |  | $obj = new WebService::Cmis::Object(repository=>$this, id=>$id, extra_params=>\%params); | 
| 570 |  |  |  |  |  |  | } catch WebService::Cmis::ClientException with { | 
| 571 |  |  |  |  |  |  | # ignore | 
| 572 |  |  |  |  |  |  | }; | 
| 573 |  |  |  |  |  |  |  | 
| 574 |  |  |  |  |  |  | return $obj; | 
| 575 |  |  |  |  |  |  | } | 
| 576 |  |  |  |  |  |  |  | 
| 577 |  |  |  |  |  |  | =item getCheckedOutDocs(%params) -> $atomFeed | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | returns a result set of cmis objects that | 
| 580 |  |  |  |  |  |  | are currently checked out. | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | See CMIS specification document 2.2.3.6 getCheckedOutDocs | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | These optional arguments are supported: | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | =over 4 | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  | =item folderId | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | =item maxItems | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | =item skipCount | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | =item orderBy | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | =item filter | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | =item includeRelationships | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | =item renditionFilter | 
| 601 |  |  |  |  |  |  |  | 
| 602 |  |  |  |  |  |  | =item includeAllowableActions | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | =back | 
| 605 |  |  |  |  |  |  |  | 
| 606 |  |  |  |  |  |  | =cut | 
| 607 |  |  |  |  |  |  |  | 
| 608 |  |  |  |  |  |  | sub getCheckedOutDocs { | 
| 609 |  |  |  |  |  |  | my $this = shift; | 
| 610 |  |  |  |  |  |  | return $this->getCollection(CHECKED_OUT_COLL, @_) | 
| 611 |  |  |  |  |  |  | } | 
| 612 |  |  |  |  |  |  |  | 
| 613 |  |  |  |  |  |  | =item getUnfiledDocs(%params): | 
| 614 |  |  |  |  |  |  |  | 
| 615 |  |  |  |  |  |  | returns a AtomFeed of cmis objects that | 
| 616 |  |  |  |  |  |  | are currently unfiled. | 
| 617 |  |  |  |  |  |  |  | 
| 618 |  |  |  |  |  |  | These optional arguments are supported: | 
| 619 |  |  |  |  |  |  |  | 
| 620 |  |  |  |  |  |  | =over 4 | 
| 621 |  |  |  |  |  |  |  | 
| 622 |  |  |  |  |  |  | =item folderId | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | =item maxItems | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | =item skipCount | 
| 627 |  |  |  |  |  |  |  | 
| 628 |  |  |  |  |  |  | =item orderBy | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | =item filter | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | =item includeRelationships | 
| 633 |  |  |  |  |  |  |  | 
| 634 |  |  |  |  |  |  | =item renditionFilter | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | =item includeAllowableActions | 
| 637 |  |  |  |  |  |  |  | 
| 638 |  |  |  |  |  |  | =back | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | =cut | 
| 641 |  |  |  |  |  |  |  | 
| 642 |  |  |  |  |  |  | sub getUnfiledDocs { | 
| 643 |  |  |  |  |  |  | my $this = shift; | 
| 644 |  |  |  |  |  |  |  | 
| 645 |  |  |  |  |  |  | unless ($this->getCapabilities->{'Unfiling'}) { | 
| 646 |  |  |  |  |  |  | throw WebService::Cmis::NotSupportedException("This repository does not support unfiling"); | 
| 647 |  |  |  |  |  |  | } | 
| 648 |  |  |  |  |  |  |  | 
| 649 |  |  |  |  |  |  | return $this->getCollection(UNFILED_COLL, @_); | 
| 650 |  |  |  |  |  |  | } | 
| 651 |  |  |  |  |  |  |  | 
| 652 |  |  |  |  |  |  | =item getTypeDefinitions(%params) -> $atomFeed | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | returns a AtomFeed of ObjectTypes holding | 
| 655 |  |  |  |  |  |  | the base types in the repository. | 
| 656 |  |  |  |  |  |  |  | 
| 657 |  |  |  |  |  |  | Use the normal paging options. | 
| 658 |  |  |  |  |  |  |  | 
| 659 |  |  |  |  |  |  | =cut | 
| 660 |  |  |  |  |  |  |  | 
| 661 |  |  |  |  |  |  | sub getTypeDefinitions { | 
| 662 |  |  |  |  |  |  | my $this = shift; | 
| 663 |  |  |  |  |  |  | return $this->getCollection(TYPES_COLL, @_); | 
| 664 |  |  |  |  |  |  | } | 
| 665 |  |  |  |  |  |  |  | 
| 666 |  |  |  |  |  |  | =item createEntryXmlDoc( | 
| 667 |  |  |  |  |  |  | summary=>$summary | 
| 668 |  |  |  |  |  |  | folder=>$parentFolder, | 
| 669 |  |  |  |  |  |  | properties=>$propsList, | 
| 670 |  |  |  |  |  |  | contentFile=>$filename, | 
| 671 |  |  |  |  |  |  | contentData=>$data, | 
| 672 |  |  |  |  |  |  | contentType=>$type | 
| 673 |  |  |  |  |  |  | ) -> $atomEntry | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | helper method that knows how to build an Atom entry based | 
| 676 |  |  |  |  |  |  | on the properties and, optionally, the contentFile provided. | 
| 677 |  |  |  |  |  |  |  | 
| 678 |  |  |  |  |  |  | =cut | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | sub createEntryXmlDoc { | 
| 681 |  |  |  |  |  |  | my $this = shift; | 
| 682 |  |  |  |  |  |  | my %params = @_; | 
| 683 |  |  |  |  |  |  |  | 
| 684 |  |  |  |  |  |  | my $xmlDoc = new XML::LibXML::Document('1.0', 'UTF-8'); | 
| 685 |  |  |  |  |  |  | my $entryElement = $xmlDoc->createElementNS(ATOM_NS, "entry"); | 
| 686 |  |  |  |  |  |  | $xmlDoc->setDocumentElement($entryElement); | 
| 687 |  |  |  |  |  |  |  | 
| 688 |  |  |  |  |  |  | $entryElement->setNamespace(APP_NS, "app", 0); | 
| 689 |  |  |  |  |  |  | $entryElement->setNamespace(CMISRA_NS, "cmisra", 0); | 
| 690 |  |  |  |  |  |  | $entryElement->setNamespace(CMIS_NS, "cmis", 0); | 
| 691 |  |  |  |  |  |  |  | 
| 692 |  |  |  |  |  |  | $entryElement->appendTextChild("summary", $params{summary}) if defined $params{summary}; | 
| 693 |  |  |  |  |  |  |  | 
| 694 |  |  |  |  |  |  | # if there is a File, encode it and add it to the XML | 
| 695 |  |  |  |  |  |  | my $contentFile = $params{contentFile}; | 
| 696 |  |  |  |  |  |  | my $contentData = $params{contentData}; | 
| 697 |  |  |  |  |  |  | if (defined $contentFile || defined $contentData) { | 
| 698 |  |  |  |  |  |  |  | 
| 699 |  |  |  |  |  |  | my $mimeType = $params{contentType}; | 
| 700 |  |  |  |  |  |  |  | 
| 701 |  |  |  |  |  |  | # read file | 
| 702 |  |  |  |  |  |  | unless (defined $contentData) { | 
| 703 |  |  |  |  |  |  | my $fh; | 
| 704 |  |  |  |  |  |  |  | 
| 705 |  |  |  |  |  |  | open($fh, '<', $contentFile) | 
| 706 |  |  |  |  |  |  | or throw Error::Simple("can't open file $contentFile"); # SMELL: use a custom exception | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | local $/ = undef;# set to read to EOF | 
| 709 |  |  |  |  |  |  | $contentData = <$fh>; | 
| 710 |  |  |  |  |  |  | close($fh); | 
| 711 |  |  |  |  |  |  | $contentData = '' unless $contentData; # no undefined | 
| 712 |  |  |  |  |  |  | } | 
| 713 |  |  |  |  |  |  |  | 
| 714 |  |  |  |  |  |  | # need to determine the mime type | 
| 715 |  |  |  |  |  |  | unless (defined $mimeType) { | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | # get the file mage used for checking | 
| 718 |  |  |  |  |  |  | unless (defined $this->{fileMage}) { | 
| 719 |  |  |  |  |  |  | require File::MMagic; | 
| 720 |  |  |  |  |  |  | $this->{fileMage} = new File::MMagic; | 
| 721 |  |  |  |  |  |  | } | 
| 722 |  |  |  |  |  |  |  | 
| 723 |  |  |  |  |  |  | $mimeType = $this->{fileMage}->checktype_contents($contentData); | 
| 724 |  |  |  |  |  |  |  | 
| 725 |  |  |  |  |  |  | # mimeType fallback | 
| 726 |  |  |  |  |  |  | $mimeType = 'application/binary' unless defined $mimeType; | 
| 727 |  |  |  |  |  |  | } | 
| 728 |  |  |  |  |  |  |  | 
| 729 |  |  |  |  |  |  | # This used to be ATOM_NS content but there is some debate among | 
| 730 |  |  |  |  |  |  | # vendors whether the ATOM_NS content must always be base64 | 
| 731 |  |  |  |  |  |  | # encoded. The spec does mandate that CMISRA_NS content be encoded | 
| 732 |  |  |  |  |  |  | # and that element takes precedence over ATOM_NS content if it is | 
| 733 |  |  |  |  |  |  | # present, so it seems reasonable to use CMIS_RA content for now | 
| 734 |  |  |  |  |  |  | # and encode everything. (comments from cmislib) | 
| 735 |  |  |  |  |  |  |  | 
| 736 |  |  |  |  |  |  | require MIME::Base64; | 
| 737 |  |  |  |  |  |  | $contentData = MIME::Base64::encode_base64($contentData); | 
| 738 |  |  |  |  |  |  |  | 
| 739 |  |  |  |  |  |  | my $contentElement = $xmlDoc->createElement('cmisra:content'); | 
| 740 |  |  |  |  |  |  | $contentElement->appendTextChild("cmisra:mediatype", $mimeType); | 
| 741 |  |  |  |  |  |  | $contentElement->appendTextChild("cmisra:base64", $contentData); | 
| 742 |  |  |  |  |  |  | $entryElement->appendChild($contentElement); | 
| 743 |  |  |  |  |  |  | } | 
| 744 |  |  |  |  |  |  |  | 
| 745 |  |  |  |  |  |  | my $objectElement = $entryElement->appendChild($xmlDoc->createElement('cmisra:object')); | 
| 746 |  |  |  |  |  |  |  | 
| 747 |  |  |  |  |  |  | if (defined $params{properties}) { | 
| 748 |  |  |  |  |  |  | my $propsElement = $objectElement->appendChild($xmlDoc->createElement('cmis:properties')); | 
| 749 |  |  |  |  |  |  |  | 
| 750 |  |  |  |  |  |  | foreach my $property (@{$params{properties}}) { | 
| 751 |  |  |  |  |  |  | #_writeCmisDebug("property=".$property->toString); | 
| 752 |  |  |  |  |  |  |  | 
| 753 |  |  |  |  |  |  | # a name is required for most things, but not for a checkout | 
| 754 |  |  |  |  |  |  | if ($property->getId eq 'cmis:name') { | 
| 755 |  |  |  |  |  |  | _writeCmisDebug("got cmis:name property"); | 
| 756 |  |  |  |  |  |  | $entryElement->appendTextChild("title", $property->getValue); | 
| 757 |  |  |  |  |  |  | } | 
| 758 |  |  |  |  |  |  |  | 
| 759 |  |  |  |  |  |  | # create property element and add to container | 
| 760 |  |  |  |  |  |  | $propsElement->appendChild($property->toXml($xmlDoc)); | 
| 761 |  |  |  |  |  |  | } | 
| 762 |  |  |  |  |  |  | } | 
| 763 |  |  |  |  |  |  |  | 
| 764 |  |  |  |  |  |  | # add folderId | 
| 765 |  |  |  |  |  |  | $objectElement->appendTextChild('cmis:folderId', $params{folder}->getId) if defined $params{folder}; | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  | # add repositoryId | 
| 768 |  |  |  |  |  |  | $objectElement->appendTextChild('cmis:repositoryId', $this->getRepositoryId); | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | #print STDERR "### created new entry:\n".$xmlDoc->toString(1)."\n###\n"; | 
| 771 |  |  |  |  |  |  | return $xmlDoc; | 
| 772 |  |  |  |  |  |  | } | 
| 773 |  |  |  |  |  |  |  | 
| 774 |  |  |  |  |  |  | =item createObject($parentFolder, properties => $propertyList, %params); | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | creates a new CMIS Objec in the given folder using | 
| 777 |  |  |  |  |  |  | the properties provided. | 
| 778 |  |  |  |  |  |  |  | 
| 779 |  |  |  |  |  |  | To specify a custom object type, pass in a Property for | 
| 780 |  |  |  |  |  |  | cmis:objectTypeId representing the type ID | 
| 781 |  |  |  |  |  |  | of the instance you want to create. If you do not pass in an object | 
| 782 |  |  |  |  |  |  | type ID, an instance of 'cmis:document' will be created. | 
| 783 |  |  |  |  |  |  |  | 
| 784 |  |  |  |  |  |  | =cut | 
| 785 |  |  |  |  |  |  |  | 
| 786 |  |  |  |  |  |  | sub createObject { | 
| 787 |  |  |  |  |  |  | my $this = shift; | 
| 788 |  |  |  |  |  |  | my $parentFolder = shift; | 
| 789 |  |  |  |  |  |  |  | 
| 790 |  |  |  |  |  |  | my $postUrl; | 
| 791 |  |  |  |  |  |  | if (defined $parentFolder) { | 
| 792 |  |  |  |  |  |  | # get the folder represented by folderId. | 
| 793 |  |  |  |  |  |  | # we'll use his 'children' link post the new child | 
| 794 |  |  |  |  |  |  | $postUrl = $parentFolder->getChildrenLink; | 
| 795 |  |  |  |  |  |  |  | 
| 796 |  |  |  |  |  |  | } else { | 
| 797 |  |  |  |  |  |  | unless ($this->getCapabilities->{'Unfiling'}) { | 
| 798 |  |  |  |  |  |  | throw WebService::Cmis::NotSupportedException("This repository does not support unfiling"); | 
| 799 |  |  |  |  |  |  | } | 
| 800 |  |  |  |  |  |  |  | 
| 801 |  |  |  |  |  |  | # post to unfiled collection | 
| 802 |  |  |  |  |  |  | $postUrl = $this->getCollectionLink(UNFILED_COLL); | 
| 803 |  |  |  |  |  |  | } | 
| 804 |  |  |  |  |  |  |  | 
| 805 |  |  |  |  |  |  | # build the Atom entry | 
| 806 |  |  |  |  |  |  | my $xmlDoc = $this->createEntryXmlDoc(folder=>$parentFolder, @_); | 
| 807 |  |  |  |  |  |  |  | 
| 808 |  |  |  |  |  |  | # post the Atom entry | 
| 809 |  |  |  |  |  |  | my $result = $this->{client}->post($postUrl, $xmlDoc->toString, ATOM_XML_ENTRY_TYPE); | 
| 810 |  |  |  |  |  |  |  | 
| 811 |  |  |  |  |  |  | # what comes back is the XML for the new document, | 
| 812 |  |  |  |  |  |  | # so use it to instantiate a new document | 
| 813 |  |  |  |  |  |  | # then return it | 
| 814 |  |  |  |  |  |  | require WebService::Cmis::Object; | 
| 815 |  |  |  |  |  |  | return new WebService::Cmis::Object(repository=>$this, xmlDoc=>$result); | 
| 816 |  |  |  |  |  |  | } | 
| 817 |  |  |  |  |  |  |  | 
| 818 |  |  |  |  |  |  | =item getTypeChildren($typeId, %params) -> $atomFeed | 
| 819 |  |  |  |  |  |  |  | 
| 820 |  |  |  |  |  |  | returns a result set ObjectType objects corresponding to the | 
| 821 |  |  |  |  |  |  | child types of the type specified by the typeId. | 
| 822 |  |  |  |  |  |  |  | 
| 823 |  |  |  |  |  |  | If no typeId is provided, the result will be the same as calling | 
| 824 |  |  |  |  |  |  | getTypeDefinitions | 
| 825 |  |  |  |  |  |  |  | 
| 826 |  |  |  |  |  |  | See CMIS specification document 2.2.2.3 getTypeChildren | 
| 827 |  |  |  |  |  |  |  | 
| 828 |  |  |  |  |  |  | These optional arguments are current supported: | 
| 829 |  |  |  |  |  |  |  | 
| 830 |  |  |  |  |  |  | =over 4 | 
| 831 |  |  |  |  |  |  |  | 
| 832 |  |  |  |  |  |  | =item includePropertyDefinitions | 
| 833 |  |  |  |  |  |  |  | 
| 834 |  |  |  |  |  |  | =item maxItems | 
| 835 |  |  |  |  |  |  |  | 
| 836 |  |  |  |  |  |  | =item skipCount | 
| 837 |  |  |  |  |  |  |  | 
| 838 |  |  |  |  |  |  | =back | 
| 839 |  |  |  |  |  |  |  | 
| 840 |  |  |  |  |  |  | =cut | 
| 841 |  |  |  |  |  |  |  | 
| 842 |  |  |  |  |  |  | sub getTypeChildren { | 
| 843 |  |  |  |  |  |  | my $this = shift; | 
| 844 |  |  |  |  |  |  | my $typeId = shift; | 
| 845 |  |  |  |  |  |  |  | 
| 846 |  |  |  |  |  |  | if (defined $typeId) { | 
| 847 |  |  |  |  |  |  | # if a typeId is specified, get it from the type definition's "down" link | 
| 848 |  |  |  |  |  |  | my $targetType = $this->getTypeDefinition($typeId); | 
| 849 |  |  |  |  |  |  | my $childrenUrl = $targetType->getLink(DOWN_REL, ATOM_XML_FEED_TYPE_P); | 
| 850 |  |  |  |  |  |  |  | 
| 851 |  |  |  |  |  |  | #print STDERR "childrenUrl=$childrenUrl\n"; | 
| 852 |  |  |  |  |  |  |  | 
| 853 |  |  |  |  |  |  | my $result = $this->{client}->get($childrenUrl, @_); | 
| 854 |  |  |  |  |  |  |  | 
| 855 |  |  |  |  |  |  | require WebService::Cmis::AtomFeed::ObjectTypes; | 
| 856 |  |  |  |  |  |  | return new WebService::Cmis::AtomFeed::ObjectTypes(repository=>$this, xmlDoc=>$result); | 
| 857 |  |  |  |  |  |  |  | 
| 858 |  |  |  |  |  |  | } else { | 
| 859 |  |  |  |  |  |  |  | 
| 860 |  |  |  |  |  |  | # otherwise, if a typeId is not specified, return | 
| 861 |  |  |  |  |  |  | # the list of base types | 
| 862 |  |  |  |  |  |  | return $this->getTypeDefinitions; | 
| 863 |  |  |  |  |  |  | } | 
| 864 |  |  |  |  |  |  | } | 
| 865 |  |  |  |  |  |  |  | 
| 866 |  |  |  |  |  |  | =item getTypeDescendants($typeId, %params) -> $atomFeed | 
| 867 |  |  |  |  |  |  |  | 
| 868 |  |  |  |  |  |  | Returns a result set ObjectType objects corresponding to the | 
| 869 |  |  |  |  |  |  | descendant types of the type specified by the typeId. | 
| 870 |  |  |  |  |  |  |  | 
| 871 |  |  |  |  |  |  | If no typeId is provided, the repository's "typesdescendants" URL | 
| 872 |  |  |  |  |  |  | will be called to determine the list of descendant types. | 
| 873 |  |  |  |  |  |  |  | 
| 874 |  |  |  |  |  |  | See CMIS specification document 2.2.2.4 getTypeDescendants | 
| 875 |  |  |  |  |  |  |  | 
| 876 |  |  |  |  |  |  | These optional arguments are supported: | 
| 877 |  |  |  |  |  |  |  | 
| 878 |  |  |  |  |  |  | =over 4 | 
| 879 |  |  |  |  |  |  |  | 
| 880 |  |  |  |  |  |  | =item depth | 
| 881 |  |  |  |  |  |  |  | 
| 882 |  |  |  |  |  |  | =item includePropertyDefinitions | 
| 883 |  |  |  |  |  |  |  | 
| 884 |  |  |  |  |  |  | =back | 
| 885 |  |  |  |  |  |  |  | 
| 886 |  |  |  |  |  |  | =cut | 
| 887 |  |  |  |  |  |  |  | 
| 888 |  |  |  |  |  |  | sub getTypeDescendants { | 
| 889 |  |  |  |  |  |  | my $this = shift; | 
| 890 |  |  |  |  |  |  | my $typeId = shift; | 
| 891 |  |  |  |  |  |  |  | 
| 892 |  |  |  |  |  |  | my $descendUrl; | 
| 893 |  |  |  |  |  |  | if (defined $typeId) { | 
| 894 |  |  |  |  |  |  | # if a typeId is specified, get it from the type definition's, "down" link | 
| 895 |  |  |  |  |  |  | my $targetType = $this->getTypeDefinition($typeId); | 
| 896 |  |  |  |  |  |  | $descendUrl = $targetType->getLink(DOWN_REL, CMIS_TREE_TYPE_P); | 
| 897 |  |  |  |  |  |  | } else { | 
| 898 |  |  |  |  |  |  | $descendUrl = $this->getLink(TYPE_DESCENDANTS_REL); | 
| 899 |  |  |  |  |  |  | } | 
| 900 |  |  |  |  |  |  |  | 
| 901 |  |  |  |  |  |  | #print STDERR "descendUrl=$descendUrl\n"; | 
| 902 |  |  |  |  |  |  |  | 
| 903 |  |  |  |  |  |  | unless (defined $descendUrl) { | 
| 904 |  |  |  |  |  |  | throw Error::Simple("Could not determine the type descendants URL"); # SMELL: do a custom exception | 
| 905 |  |  |  |  |  |  | } | 
| 906 |  |  |  |  |  |  |  | 
| 907 |  |  |  |  |  |  | my $result = $this->{client}->get($descendUrl, @_); | 
| 908 |  |  |  |  |  |  |  | 
| 909 |  |  |  |  |  |  | require WebService::Cmis::AtomFeed::ObjectTypes; | 
| 910 |  |  |  |  |  |  | return new WebService::Cmis::AtomFeed::ObjectTypes(repository=>$this, xmlDoc=>$result); | 
| 911 |  |  |  |  |  |  | } | 
| 912 |  |  |  |  |  |  |  | 
| 913 |  |  |  |  |  |  | =item query(statement, %params): | 
| 914 |  |  |  |  |  |  |  | 
| 915 |  |  |  |  |  |  | Returns a result set of CMIS Objects based on the CMIS | 
| 916 |  |  |  |  |  |  | Query Language passed in as the statement. The actual objects | 
| 917 |  |  |  |  |  |  | returned will be instances of the appropriate child class based | 
| 918 |  |  |  |  |  |  | on the object's base type ID. | 
| 919 |  |  |  |  |  |  |  | 
| 920 |  |  |  |  |  |  | In order for the results to be properly instantiated as objects, | 
| 921 |  |  |  |  |  |  | make sure you include 'cmis:objectId' as one of the fields in | 
| 922 |  |  |  |  |  |  | your select statement, or just use "SELECT \*". | 
| 923 |  |  |  |  |  |  |  | 
| 924 |  |  |  |  |  |  | If you want the search results to automatically be instantiated with | 
| 925 |  |  |  |  |  |  | the appropriate sub-class of CMIS Object you must either | 
| 926 |  |  |  |  |  |  | include cmis:baseTypeId as one of the fields in your select statement | 
| 927 |  |  |  |  |  |  | or just use "SELECT \*". | 
| 928 |  |  |  |  |  |  |  | 
| 929 |  |  |  |  |  |  | See CMIS specification document 2.2.6.1 query | 
| 930 |  |  |  |  |  |  |  | 
| 931 |  |  |  |  |  |  | The following optional arguments are supported: | 
| 932 |  |  |  |  |  |  |  | 
| 933 |  |  |  |  |  |  | =over 4 | 
| 934 |  |  |  |  |  |  |  | 
| 935 |  |  |  |  |  |  | =item searchAllVersions | 
| 936 |  |  |  |  |  |  |  | 
| 937 |  |  |  |  |  |  | =item includeRelationships | 
| 938 |  |  |  |  |  |  |  | 
| 939 |  |  |  |  |  |  | =item renditionFilter | 
| 940 |  |  |  |  |  |  |  | 
| 941 |  |  |  |  |  |  | =item includeAllowableActions | 
| 942 |  |  |  |  |  |  |  | 
| 943 |  |  |  |  |  |  | =item maxItems | 
| 944 |  |  |  |  |  |  |  | 
| 945 |  |  |  |  |  |  | =item skipCount | 
| 946 |  |  |  |  |  |  |  | 
| 947 |  |  |  |  |  |  | =back | 
| 948 |  |  |  |  |  |  |  | 
| 949 |  |  |  |  |  |  | =cut | 
| 950 |  |  |  |  |  |  |  | 
| 951 |  |  |  |  |  |  | sub query { | 
| 952 |  |  |  |  |  |  | my $this = shift; | 
| 953 |  |  |  |  |  |  | my $statement = shift; | 
| 954 |  |  |  |  |  |  |  | 
| 955 |  |  |  |  |  |  | # get the URL this repository uses to accept query POSTs | 
| 956 |  |  |  |  |  |  | my $queryUrl = $this->getCollectionLink(QUERY_COLL); | 
| 957 |  |  |  |  |  |  |  | 
| 958 |  |  |  |  |  |  | # build the CMIS query XML that we're going to POST | 
| 959 |  |  |  |  |  |  | my $xmlDoc = $this->_getQueryXmlDoc($statement, @_); | 
| 960 |  |  |  |  |  |  |  | 
| 961 |  |  |  |  |  |  | # do the POST | 
| 962 |  |  |  |  |  |  | my $result = $this->{client}->post($queryUrl, $xmlDoc->toString, CMIS_QUERY_TYPE); | 
| 963 |  |  |  |  |  |  |  | 
| 964 |  |  |  |  |  |  | # return the result set | 
| 965 |  |  |  |  |  |  | require WebService::Cmis::AtomFeed::Objects; | 
| 966 |  |  |  |  |  |  | return new WebService::Cmis::AtomFeed::Objects(repository=>$this, xmlDoc=>$result); | 
| 967 |  |  |  |  |  |  | } | 
| 968 |  |  |  |  |  |  |  | 
| 969 |  |  |  |  |  |  | # Utility method that knows how to build CMIS query xml around the specified query statement. | 
| 970 |  |  |  |  |  |  | sub _getQueryXmlDoc { | 
| 971 |  |  |  |  |  |  | my $this = shift; | 
| 972 |  |  |  |  |  |  | my $statement = shift; | 
| 973 |  |  |  |  |  |  | my %params = @_; | 
| 974 |  |  |  |  |  |  |  | 
| 975 |  |  |  |  |  |  | my $xmlDoc = new XML::LibXML::Document('1.0', 'UTF-8'); | 
| 976 |  |  |  |  |  |  |  | 
| 977 |  |  |  |  |  |  | my $queryElement = $xmlDoc->createElementNS(CMIS_NS, "query"); | 
| 978 |  |  |  |  |  |  |  | 
| 979 |  |  |  |  |  |  | $xmlDoc->setDocumentElement($queryElement); | 
| 980 |  |  |  |  |  |  |  | 
| 981 |  |  |  |  |  |  | my $statementElement = $xmlDoc->createElementNS(CMIS_NS, "statement"); | 
| 982 |  |  |  |  |  |  | $statementElement->addChild($xmlDoc->createCDATASection($statement)); | 
| 983 |  |  |  |  |  |  | $queryElement->appendChild($statementElement); | 
| 984 |  |  |  |  |  |  |  | 
| 985 |  |  |  |  |  |  | foreach my $key (keys %params) { | 
| 986 |  |  |  |  |  |  | my $optionElement = $xmlDoc->createElementNS(CMIS_NS, $key); | 
| 987 |  |  |  |  |  |  | $optionElement->appendText($params{$key}); | 
| 988 |  |  |  |  |  |  | $queryElement->appendChild($optionElement); | 
| 989 |  |  |  |  |  |  | } | 
| 990 |  |  |  |  |  |  |  | 
| 991 |  |  |  |  |  |  | #_writeCmisDebug("query:\n".$xmlDoc->toString(1)); | 
| 992 |  |  |  |  |  |  |  | 
| 993 |  |  |  |  |  |  | return $xmlDoc; | 
| 994 |  |  |  |  |  |  | } | 
| 995 |  |  |  |  |  |  |  | 
| 996 |  |  |  |  |  |  | =item getLatestChangeLogToken () -> $token | 
| 997 |  |  |  |  |  |  |  | 
| 998 |  |  |  |  |  |  | returns a token to ge use fetching a changes atom feed. | 
| 999 |  |  |  |  |  |  |  | 
| 1000 |  |  |  |  |  |  | =cut | 
| 1001 |  |  |  |  |  |  |  | 
| 1002 |  |  |  |  |  |  | sub getLatestChangeLogToken { | 
| 1003 |  |  |  |  |  |  | my $this = shift; | 
| 1004 |  |  |  |  |  |  |  | 
| 1005 |  |  |  |  |  |  | unless ($this->getCapabilities()->{'Changes'}) { | 
| 1006 |  |  |  |  |  |  | throw WebService::Cmis::NotSupportedException("This repository does not support change logs"); | 
| 1007 |  |  |  |  |  |  | } | 
| 1008 |  |  |  |  |  |  |  | 
| 1009 |  |  |  |  |  |  | return $$this->getRepositoryInfo->{latestChangeLogToken}; | 
| 1010 |  |  |  |  |  |  | } | 
| 1011 |  |  |  |  |  |  |  | 
| 1012 |  |  |  |  |  |  | =item getContentChanges(%params) -> $atomFeed | 
| 1013 |  |  |  |  |  |  |  | 
| 1014 |  |  |  |  |  |  | returns a AtomFeed containing ChangeEntry objects. | 
| 1015 |  |  |  |  |  |  |  | 
| 1016 |  |  |  |  |  |  | See CMIS specification document 2.2.6.2 getContentChanges | 
| 1017 |  |  |  |  |  |  |  | 
| 1018 |  |  |  |  |  |  | The following optional arguments are supported: | 
| 1019 |  |  |  |  |  |  |  | 
| 1020 |  |  |  |  |  |  | =over 4 | 
| 1021 |  |  |  |  |  |  |  | 
| 1022 |  |  |  |  |  |  | =item changeLogToken | 
| 1023 |  |  |  |  |  |  |  | 
| 1024 |  |  |  |  |  |  | =item includeProperties | 
| 1025 |  |  |  |  |  |  |  | 
| 1026 |  |  |  |  |  |  | =item includePolicyIDs | 
| 1027 |  |  |  |  |  |  |  | 
| 1028 |  |  |  |  |  |  | =item includeACL | 
| 1029 |  |  |  |  |  |  |  | 
| 1030 |  |  |  |  |  |  | =item maxItems | 
| 1031 |  |  |  |  |  |  |  | 
| 1032 |  |  |  |  |  |  | =back | 
| 1033 |  |  |  |  |  |  |  | 
| 1034 |  |  |  |  |  |  | You can get the latest change log token by inspecting the repository | 
| 1035 |  |  |  |  |  |  | info via Repository.getRepositoryInfo. | 
| 1036 |  |  |  |  |  |  |  | 
| 1037 |  |  |  |  |  |  | =cut | 
| 1038 |  |  |  |  |  |  |  | 
| 1039 |  |  |  |  |  |  | sub getContentChanges { | 
| 1040 |  |  |  |  |  |  | my $this = shift; | 
| 1041 |  |  |  |  |  |  | my %params = @_; | 
| 1042 |  |  |  |  |  |  |  | 
| 1043 |  |  |  |  |  |  | #$params{changeLogToken} = $this->getLatestChangeLogToken() unless defined $params{changeLogToken}; | 
| 1044 |  |  |  |  |  |  |  | 
| 1045 |  |  |  |  |  |  | unless ($this->getCapabilities()->{'Changes'}) { | 
| 1046 |  |  |  |  |  |  | throw WebService::Cmis::NotSupportedException("This repository does not support change logs"); | 
| 1047 |  |  |  |  |  |  | } | 
| 1048 |  |  |  |  |  |  |  | 
| 1049 |  |  |  |  |  |  | my $changesUrl = $this->getLink(CHANGE_LOG_REL); | 
| 1050 |  |  |  |  |  |  | my $result = $this->{client}->get($changesUrl, %params); | 
| 1051 |  |  |  |  |  |  |  | 
| 1052 |  |  |  |  |  |  | # return the result set | 
| 1053 |  |  |  |  |  |  | require WebService::Cmis::AtomFeed::ChangeEntries; | 
| 1054 |  |  |  |  |  |  | return new WebService::Cmis::AtomFeed::ChangeEntries(repository=>$this, xmlDoc=>$result); | 
| 1055 |  |  |  |  |  |  | } | 
| 1056 |  |  |  |  |  |  |  | 
| 1057 |  |  |  |  |  |  | =item createDocument( | 
| 1058 |  |  |  |  |  |  | $name, | 
| 1059 |  |  |  |  |  |  | folder=>$parentFolder, | 
| 1060 |  |  |  |  |  |  | properties=>$propsList, | 
| 1061 |  |  |  |  |  |  | contentFile=>$filename, | 
| 1062 |  |  |  |  |  |  | contentData=>$data, | 
| 1063 |  |  |  |  |  |  | contentType=>$type, | 
| 1064 |  |  |  |  |  |  | ) -> $cmisDocument | 
| 1065 |  |  |  |  |  |  |  | 
| 1066 |  |  |  |  |  |  | creates a new Document object in the parent folder provided or filed to the | 
| 1067 |  |  |  |  |  |  | Unfiled collection of the repository. | 
| 1068 |  |  |  |  |  |  |  | 
| 1069 |  |  |  |  |  |  | The method will attempt to guess the appropriate content type and encoding | 
| 1070 |  |  |  |  |  |  | based on the file. To specify it yourself, pass them in via the contentType and | 
| 1071 |  |  |  |  |  |  |  | 
| 1072 |  |  |  |  |  |  | To specify a custom object type, pass in a Property for cmis:objectTypeId | 
| 1073 |  |  |  |  |  |  | representing the type ID of the instance you want to create. If you do not pass | 
| 1074 |  |  |  |  |  |  | in an object type ID, an instance of 'cmis:document' will be created. | 
| 1075 |  |  |  |  |  |  |  | 
| 1076 |  |  |  |  |  |  | See CMIS specification document 2.2.4.1 createDument | 
| 1077 |  |  |  |  |  |  |  | 
| 1078 |  |  |  |  |  |  | =cut | 
| 1079 |  |  |  |  |  |  |  | 
| 1080 |  |  |  |  |  |  | sub createDocument { | 
| 1081 |  |  |  |  |  |  | my $this = shift; | 
| 1082 |  |  |  |  |  |  | my $name = shift; | 
| 1083 |  |  |  |  |  |  | my %params = @_; | 
| 1084 |  |  |  |  |  |  |  | 
| 1085 |  |  |  |  |  |  | my $parentFolder = delete $params{folder}; | 
| 1086 |  |  |  |  |  |  | my $properties = delete $params{properties}; | 
| 1087 |  |  |  |  |  |  | $properties = [] unless defined $properties; | 
| 1088 |  |  |  |  |  |  |  | 
| 1089 |  |  |  |  |  |  | # construct properties | 
| 1090 |  |  |  |  |  |  | require WebService::Cmis::Property; | 
| 1091 |  |  |  |  |  |  |  | 
| 1092 |  |  |  |  |  |  | push @$properties, WebService::Cmis::Property::newString( | 
| 1093 |  |  |  |  |  |  | id => 'cmis:name', | 
| 1094 |  |  |  |  |  |  | value => $name, | 
| 1095 |  |  |  |  |  |  | ); | 
| 1096 |  |  |  |  |  |  |  | 
| 1097 |  |  |  |  |  |  | my $foundObjectTypeId = 0; | 
| 1098 |  |  |  |  |  |  | foreach my $prop (@$properties) { | 
| 1099 |  |  |  |  |  |  | if ($prop->getId eq 'cmis:objectTypeId') { | 
| 1100 |  |  |  |  |  |  | $foundObjectTypeId = 1; | 
| 1101 |  |  |  |  |  |  | last; | 
| 1102 |  |  |  |  |  |  | } | 
| 1103 |  |  |  |  |  |  | } | 
| 1104 |  |  |  |  |  |  |  | 
| 1105 |  |  |  |  |  |  | unless ($foundObjectTypeId) { | 
| 1106 |  |  |  |  |  |  | push @$properties, WebService::Cmis::Property::newId( | 
| 1107 |  |  |  |  |  |  | id => 'cmis:objectTypeId', | 
| 1108 |  |  |  |  |  |  | value => 'cmis:document', | 
| 1109 |  |  |  |  |  |  | ); | 
| 1110 |  |  |  |  |  |  | } | 
| 1111 |  |  |  |  |  |  |  | 
| 1112 |  |  |  |  |  |  | # create the object | 
| 1113 |  |  |  |  |  |  | return $this->createObject( | 
| 1114 |  |  |  |  |  |  | $parentFolder, | 
| 1115 |  |  |  |  |  |  | properties=>$properties, | 
| 1116 |  |  |  |  |  |  | %params, | 
| 1117 |  |  |  |  |  |  | ); | 
| 1118 |  |  |  |  |  |  | } | 
| 1119 |  |  |  |  |  |  |  | 
| 1120 |  |  |  |  |  |  |  | 
| 1121 |  |  |  |  |  |  | =item createFolder($name, folder=>$parentFolder, properties=>$propertyList, %params) -> $cmisFolder | 
| 1122 |  |  |  |  |  |  |  | 
| 1123 |  |  |  |  |  |  | creates a new CMIS Folder using the properties provided. | 
| 1124 |  |  |  |  |  |  |  | 
| 1125 |  |  |  |  |  |  | To specify a custom folder type, pass in a property called | 
| 1126 |  |  |  |  |  |  | cmis:objectTypeId representing the type ID | 
| 1127 |  |  |  |  |  |  | of the instance you want to create. If you do not pass in an object | 
| 1128 |  |  |  |  |  |  | type ID, an instance of 'cmis:folder' will be created. | 
| 1129 |  |  |  |  |  |  |  | 
| 1130 |  |  |  |  |  |  | my $rootFolder = $repo->getRootFolder; | 
| 1131 |  |  |  |  |  |  |  | 
| 1132 |  |  |  |  |  |  | my $subFolder = $rootFolder->createFolder( | 
| 1133 |  |  |  |  |  |  | 'My new folder', | 
| 1134 |  |  |  |  |  |  | summary => "This is my new test folder." | 
| 1135 |  |  |  |  |  |  | ); | 
| 1136 |  |  |  |  |  |  |  | 
| 1137 |  |  |  |  |  |  | my $repo = $repo->createFolder( | 
| 1138 |  |  |  |  |  |  | 'My other folder', | 
| 1139 |  |  |  |  |  |  | folder => $rootFolder, | 
| 1140 |  |  |  |  |  |  | summary => "This is my other test folder." | 
| 1141 |  |  |  |  |  |  | ); | 
| 1142 |  |  |  |  |  |  |  | 
| 1143 |  |  |  |  |  |  | See CMIS specification document 2.2.4.3 createFolder | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 |  |  |  |  |  |  | =cut | 
| 1146 |  |  |  |  |  |  |  | 
| 1147 |  |  |  |  |  |  | sub createFolder { | 
| 1148 |  |  |  |  |  |  | my $this = shift; | 
| 1149 |  |  |  |  |  |  | my $name = shift; | 
| 1150 |  |  |  |  |  |  | my %params = @_; | 
| 1151 |  |  |  |  |  |  |  | 
| 1152 |  |  |  |  |  |  | my $parentFolder = delete $params{folder}; | 
| 1153 |  |  |  |  |  |  |  | 
| 1154 |  |  |  |  |  |  | my $properties = delete $params{properties}; | 
| 1155 |  |  |  |  |  |  | $properties = [] unless defined $properties; | 
| 1156 |  |  |  |  |  |  |  | 
| 1157 |  |  |  |  |  |  | # construct properties | 
| 1158 |  |  |  |  |  |  | require WebService::Cmis::Property; | 
| 1159 |  |  |  |  |  |  |  | 
| 1160 |  |  |  |  |  |  | push @$properties, | 
| 1161 |  |  |  |  |  |  | WebService::Cmis::Property::newString( | 
| 1162 |  |  |  |  |  |  | id => 'cmis:name', | 
| 1163 |  |  |  |  |  |  | value => $name, | 
| 1164 |  |  |  |  |  |  | ); | 
| 1165 |  |  |  |  |  |  |  | 
| 1166 |  |  |  |  |  |  | my $foundObjectTypeId = 0; | 
| 1167 |  |  |  |  |  |  | foreach my $prop (@$properties) { | 
| 1168 |  |  |  |  |  |  | if ($prop->getId eq 'cmis:objectTypeId') { | 
| 1169 |  |  |  |  |  |  | $foundObjectTypeId = 1; | 
| 1170 |  |  |  |  |  |  | last; | 
| 1171 |  |  |  |  |  |  | } | 
| 1172 |  |  |  |  |  |  | } | 
| 1173 |  |  |  |  |  |  |  | 
| 1174 |  |  |  |  |  |  | unless ($foundObjectTypeId) { | 
| 1175 |  |  |  |  |  |  | push @$properties, | 
| 1176 |  |  |  |  |  |  | WebService::Cmis::Property::newId( | 
| 1177 |  |  |  |  |  |  | id => 'cmis:objectTypeId', | 
| 1178 |  |  |  |  |  |  | value => 'cmis:folder', | 
| 1179 |  |  |  |  |  |  | ); | 
| 1180 |  |  |  |  |  |  | } | 
| 1181 |  |  |  |  |  |  |  | 
| 1182 |  |  |  |  |  |  | # create the object | 
| 1183 |  |  |  |  |  |  | return $this->createObject( | 
| 1184 |  |  |  |  |  |  | $parentFolder, | 
| 1185 |  |  |  |  |  |  | properties => $properties, | 
| 1186 |  |  |  |  |  |  | %params, | 
| 1187 |  |  |  |  |  |  | ); | 
| 1188 |  |  |  |  |  |  | } | 
| 1189 |  |  |  |  |  |  |  | 
| 1190 |  |  |  |  |  |  | =item createRelationship | 
| 1191 |  |  |  |  |  |  |  | 
| 1192 |  |  |  |  |  |  | TODO: This is not yet implemented. | 
| 1193 |  |  |  |  |  |  |  | 
| 1194 |  |  |  |  |  |  | =cut | 
| 1195 |  |  |  |  |  |  |  | 
| 1196 |  |  |  |  |  |  | sub createRelationship { throw WebService::Cmis::NotImplementedException; } | 
| 1197 |  |  |  |  |  |  |  | 
| 1198 |  |  |  |  |  |  | =item createPolicy | 
| 1199 |  |  |  |  |  |  |  | 
| 1200 |  |  |  |  |  |  | TODO: This is not yet implemented. | 
| 1201 |  |  |  |  |  |  |  | 
| 1202 |  |  |  |  |  |  | =cut | 
| 1203 |  |  |  |  |  |  |  | 
| 1204 |  |  |  |  |  |  | sub createPolicy { throw WebService::Cmis::NotImplementedException; } | 
| 1205 |  |  |  |  |  |  |  | 
| 1206 |  |  |  |  |  |  | =back | 
| 1207 |  |  |  |  |  |  |  | 
| 1208 |  |  |  |  |  |  | =head1 AUTHOR | 
| 1209 |  |  |  |  |  |  |  | 
| 1210 |  |  |  |  |  |  | Michael Daum C<<  >> | 
| 1211 |  |  |  |  |  |  |  | 
| 1212 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 1213 |  |  |  |  |  |  |  | 
| 1214 |  |  |  |  |  |  | Copyright 2012-2013 Michael Daum | 
| 1215 |  |  |  |  |  |  |  | 
| 1216 |  |  |  |  |  |  | This module is free software; you can redistribute it and/or modify it under | 
| 1217 |  |  |  |  |  |  | the same terms as Perl itself.  See F. | 
| 1218 |  |  |  |  |  |  |  | 
| 1219 |  |  |  |  |  |  | =cut | 
| 1220 |  |  |  |  |  |  |  | 
| 1221 |  |  |  |  |  |  |  | 
| 1222 |  |  |  |  |  |  | 1; | 
| 1223 |  |  |  |  |  |  |  |