# encoding: ascii
# api: powershell
# title: DekiWiki Module
# description: The first of many script cmdlets for working with DekiWiki
# version: 1.5
# type: module
# license: CC0
# function: Set-DekiUrl
# x-poshcode-id: 692
# x-archived: 2010-05-08T05:18:14
#
# Depends on the HttpRest script-module: http://poshcode.org/691
#
## DekiWiki Module 1.5
#require -version 2.0
## Depends on the HttpRest script-module:
## http :// huddledmasses.org/using-rest-apis-from-powershell-with-the-dream-sdk/
####################################################################################################
## The first of many script cmdlets for working with DekiWiki, based on the HttpREST module
##
## For documentation of the DekiWiki REST API:
## http :// wiki.developer.mindtouch.com/MindTouch_Deki/API_Reference
####################################################################################################
## USAGE:
## Add-Module DekiWiki
## Set-DekiUrl http`://powershell.wik.is
## ...
## ## For usage of each cmdlet, see the comments above each individual function ## ## ## ## ## ## ##
####################################################################################################
## History:
## v 1.5 Rewrite with the "Dream" specific code extracted to the HttpRest module
## v 1.2 Remove-DekiFile
## v 1.0 Set-DekiCredential, Get-DekiContent, Set-DekiContent, New-DekiContent, Set-DekiFile
##
$hr = Add-Module HttpRest -Passthru
Add-Module "$PSScriptRoot\Utilities.ps1"
$url = "http://powershell.wik.is"
$api = "$url/@api/deki"
#New-Alias Set-DekiCredential Set-HttpCredential -EA "SilentlyContinue"
#New-Alias Set-DekiUrl Set-HttpDefaultUrl -EA "SilentlyContinue"
FUNCTION Set-DekiUrl {
PARAM ([uri]$baseUri=$(Read-Host "Please enter the base Uri for your RESTful web-service"))
Set-HttpDefaultUrl $baseUri
}
FUNCTION Set-DekiCredential {
PARAM($Credential=$(Get-Credential -Title "Http Authentication Request - $($global:url.Host)" `
-Message "Your login for $($global:url.Host)" `
-Domain $global:url.Host ))
Set-HttpCredential $Credential
}
New-Alias e2 Encode-Twice -EA "SilentlyContinue"
Add-Type '
public class ModuleInfo {
public string Name;
public string[] Author;
public string CompanyName;
public string[] Copyright;
public string[] Description;
public System.Version ModuleVersion;
public string[] RequiredAssemblies;
public string[] Dependencies;
public System.Guid GUID = System.Guid.NewGuid();
public string[] PowerShellVersion;
public string[] ModulesToProcess;
public System.Version CLRVersion;
public string[] FormatsToProcess;
public string[] TypesToProcess;
public string[] OtherItems;
public string ModuleFile;
}
' -EA "SilentlyContinue"
# Retrieve a sitemap of all the pages in a wiki,
# OR Retrieve all the subpages of a page as a sitemap
# Added by Mark E. Schill
CMDLET Get-DekiSiteMap {
PARAM(
[Parameter(Position=0, Mandatory=$false)]
[string]
$StartPage
,
[Parameter(Position=1, Mandatory=$false)]
[ValidateSet("xml","html","google")]
[string]
$Format = "xml"
)
if($StartPage) {
Invoke-Http GET "pages/=$(e2 $StartPage)/tree" @{"format"=$format} | Receive-Http -Out Xml
} else {
Invoke-Http GET "pages" @{"format"=$format} | Receive-Http -Out Xml
}
}
# Get the contents of a page from a DekiWiki
# Note that by default you retrieve the "view" (rendered) markup
# If you want to see it before the extensions run, you should specify at least -mode viewnoexecute
# If you want to see the source so you can make changes, be sure to specify -mode edit
CMDLET Get-DekiContent {
PARAM(
[Parameter(Position=0, Mandatory=$true)]
[string]
$pageName
,
[Parameter(Position=1, Mandatory=$false)]
[int]
$section
,
[Parameter(Position=5, Mandatory=$false)]
[ValidateSet("edit", "raw", "view", "viewnoexecute")]
[string]
$mode="view"
)
Invoke-Http "GET" "pages/=$(e2 $pageName)/contents" @{mode=$mode;section=$section} | Receive-Http -Out Xml
}
# Get-DekiFile will LIST all files if called with just a PageName
# Otherwise, fileName can be a single fileName, or wildcards
# To download all the attachments on a page, try:
# Get-DekiFile PageName | Get-DekiFile
CMDLET Get-DekiFile {
PARAM(
[Parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[string]
$pageName
,
[Parameter(Position=1, Mandatory=$false, ValueFromPipelineByPropertyName=$true)]
[string]
$fileName
,
[Parameter(Position=2, Mandatory=$false)]
[string]
$destination
)
PROCESS {
## Remember, we can get both on the pipeline, so ...
$files = Invoke-Http "GET" "pages/=$(e2 $pageName)/files" | Receive-Http Xml //file
Write-Verbose "Fetching $($fileName) from the $($files.Count) in $pageName"
if(!$fileName) {
## Add a PageName property, because then you can pipe the output back to Get-DekiFile after filtering...
write-output $files | Add-Member NoteProperty PageName $pageName -passthru
} else {
## Using -like means $fileName allows globbing
foreach($file in $($files | where { $_.filename -like $fileName } )) {
Invoke-Http "GET" "pages/=$(e2 $pageName)/files/=$(e2 $file.filename)" | Receive-Http File $(Get-FileName $file.filename $destination)
}
}
}
}
# Set the contents of a DekiWiki page
# Note that you can pass the content as an XML document, plain text, or a filename...
CMDLET Set-DekiContent {
PARAM(
[Parameter(Position=0, Mandatory=$true)]
[string]
$pageName
,
[Parameter(Position=1, Mandatory=$true,
ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias("FullName")]
[string]
$content
,
[Parameter(Position=2, Mandatory=$false)]
$section
,
[switch]$new
,
[switch]$append
)
$with = @{"edittime"=$(Get-UtcTime "yyyyMMddhhmmss")}
if($new) {
$with.abort = "exists"
}
if($append) {
$with.section = "0"
} elseif($section) {
$with.section = $section
}
$result = Invoke-Http POST "pages/=$(e2 $pageName)/contents" -With $with -Content $content -Auth $true -Wait
if($result.IsSuccessful) {
return "$url/$($result | Receive-Http Text //edit/page/path)"
} else {
return $result
}
}
# Same as Set-DekiContent, except this one crashes if the page already exists.
# This is technically the only safe way to create new pages without fear of conflicts...
CMDLET New-DekiContent {
PARAM(
[Parameter(Position=0, Mandatory=$true)]
[string]$pageName
,
[Parameter(Position=1, Mandatory=$true,
ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias("FullName")]
[string]
$content
)
PROCESS {
Set-DekiContent $pageName $content -new
}
}
# Add a new attachment (or a new version of an existing attachment)
# You can use wildcards, and specify arrays. But you could also include descriptions
# USAGE:
## Set-DekiFile Path/PageName *.ps1,*.psm1
## Set-DekiFile Path/PageName @{"Neat.jpg"="A really cool screenshot";"Source.zip"="The source code"}
## Set-DekiFile Path/PageName Something.png
## ls *.png | Set-DekiFile Path/PageName
CMDLET Set-DekiFile {
param(
[Parameter(Position=0, Mandatory=$true)]
[string]$pageName
,
[Parameter(Position=1, Mandatory=$true,
ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias("FullName")]
$Files
)
# Two ways to specify files: 1) hashtable: file = "description" 1) array: file, file, file
$hasDescriptions = $false;
if($files -is [Hashtable]) {
$hasDescriptions = $true;
$fileNames = $files.Keys
} else {
$fileNames = $files
}
foreach($fileName in $fileNames) {
foreach($file in (gci $fileName)) {
$with = @{}
if($hasDescriptions) {
$With.description = $files[$fileName]
}
$result = Http-Invoke PUT "pages/=$(e2 $pageName)/files/=$($file.Name)" $With $file.FullName -Wait
if($result.IsSuccessful) {
return ($result | Receive-Http Text //file/href)
} else {
return $result.Response
}
}
}
}
## Move a page from one place to another (leaving an "alias" placeholder)
CMDLET Move-DekiContent {
PARAM(
[Parameter(Position=0, Mandatory=$true)]
[string]
$pageName
,
[Parameter(Position=1, Mandatory=$true)]
[string]
$newPageName
)
$with = @{"to"="$newPageName"}
$result = Invoke-Http POST "pages/=$(e2 $pageName)/move" -With $with -Auth $true -Wait
if($result.IsSuccessful) {
return "$url/$($result | Receive-Http Text '//page[1]/path' )"
} else {
return $result
}
}
CMDLET New-DekiAlias {
PARAM(
[Parameter(Position=0, Mandatory=$true)]
[string]
$pageName
,
[Parameter(Position=1, Mandatory=$true)]
[string]
$Alias
)
$result = Invoke-Http POST "pages/=$(e2 $pageName)/move" @{"to"="$Alias"} -Auth $true -Wait
if($result.IsSuccessful) {
$aliasPage = "$url/$($result | Receive-Http Text '//page[1]/path' )"
$result = Invoke-Http POST "pages/=$(e2 $Alias)/move" @{"to"="$pageName"} -Auth $true -Wait
if($result.IsSuccessful) {
return $aliasPage
} else {
return $result
}
} else {
return $result
}
}
# Delete a page from a dekiwiki, with an option to RECURSIVELY delete all child pages.
# NOTE: if you delete a page that has children, and don't recurse, the contents are removed
# and then replaced with template text, because they can't go away completely
# NOTE: if you delete a page, it is actually put in the archive
# TODO: add a -Force parameter to allow them to be permanently deleted from the archive
CMDLET Remove-DekiContent {
PARAM(
[Parameter(Position=0, Mandatory=$true,ValueFromPipelineByPropertyName=$true)][string]$pageName
,
[Parameter(Position=2, Mandatory=$false)][switch]$recurse
)
Http-Invoke DELETE "pages/=$(e2 $pageName)" @{recursive=$($recurse.ToBool())}
if($result.IsSuccessful) {
return "DELETED $url/$PageName"
} else {
return $result.Response
}
}
# Delete a file attachment from a dekiwiki page
# You can use wildcards, and specify arrays:
## USAGE:
## Remove-DekiFile Path/PageName *.ps1,*.psm1
## Remove-DekiFile Path/PageName Something.png
## ls *.png | Remove-DekiFile Path/PageName
CMDLET Remove-DekiFile {
PARAM(
[Parameter(Position=0, Mandatory=$true,ValueFromPipelineByPropertyName=$true)][string]$pageName
,
[Parameter(Position=1, Mandatory=$true,
ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias("Name")]
$fileNames
)
$files = Invoke-Http GET "pages/=$(e2 $pageName)/files" | Receive-Http Text //file/filename
foreach($fileName in $fileNames) {
foreach($file in $($files -like $fileName)) {
$path = "pages/=$(e2 $pageName)/files/=$(e2 $file)"
Write-Host $path -fore cyan
$result = Invoke-Http DELETE $path -Auth $true -Wait
if($result.IsSuccessful) {
"DELETED: $api/$path"
} else {
$result.Response
}
}
}
}
Export-ModuleMember Set-DekiCredential, Set-DekiUrl,
Get-HtmlHelp, Get-DekiFile, Get-DekiSiteMap, Get-DekiContent,
Set-DekiContent, New-DekiContent,
Move-DekiContent, New-DekiAlias,
Remove-DekiContent, Remove-DekiFile,
Set-DekiFile