新 サブサイト→トップレベルサイト - (1) - [技術情報]
ご無沙汰してます。
しばらく更新してませんでしたがまた徐々に更新をしていきたいと思います。
まずはおわびから。一番最初に書いた記事について間違いがありました。(http://blog.so-net.ne.jp/SharePoint_Programing/2007-05-15) この方法ではワークスペースが移行できなかったり、エラーになる場合があり、実際の現場では使えませんでした。不完全な記事を掲載してしまい申し訳ありません。
そこで、できるかはわかりませんがサブサイト→トップレベルサイトの移行ツールを作成してみようと思います。(懲りもせず自分で試行錯誤の末、完成させようと考えています。間違っている可能性も多々あるとは思いますがご容赦ください) m(_ _)m
まずは要件として大きすぎるコンテンツDBのサブサイトを別のコンテンツDBに移行するというものを考えます。実現方法は自分が得意とするWindowsAPPで実現しようと思います。WSSではトップレベルサイトはSPSite、サブサイトはSPWebのオブジェクトになるため、移行先にはダミーでトップレベルサイトを作成し、サブサイト同士でオブジェクトをコピーしようと思います。
まずはコンテンツDBの作成です。
Dim MySPGlobalAdmin as New SPGlobalAdmin
Dim MySPVirtualServer As SPVirtualServer _
= MySPGlobalAdmin.OpenVirtualServer(VirtualServerUri)
Dim strDatabaseServer as string _
= MySPVirtualServer.ContentsDB(0).Server
Dim MySPLanguage as SPLanguage = MySPGlobalAdmin.ServerLanguage
Dim strDatabaseName as string = "NewConDB"
Dim strUserName as string = nothing
Dim strPassword as string = nothing
Dim nWarningSiteCount as integer = 1
Dim nMaximumSiteCount as integer = 1
Dim nStatus = 0
Dim strSiteUrl as string = "Site Url"
Dim strTitle as string ="テスト"
Dim strDescription as string = "サイトコレクション作成のテスト"
Dim nLCID as System.Uint32 = Convert.ToUInt32(MySPLanguage.LCID)
Dim strWebTemplate as string = "STS#0"
Dim strOwnerLogin as string = "オーナーログイン名"
Dim strOwnerName as string = "オーナー名"
Dim strOwnerEmail as string = "オーナーメールアドレス"
'--- コンテンツDB作成
MySPVirtualServer.ContentDatabases.Add( _
strDatabaseServer, strDatabaseName, strUsername, _
strPassword, nWarningSiteCount, nMaximumSiteCount, nStatus)
'--- サイトコレクション作成
Dim NewSite As SPSite = MySPVirtualServer.Sites.Add( strSiteUrl, strTitle, _
strDescription, nLCID, strWebTemplate, strOwnerLogin, strOwnerName, _
strOwnerEmail)
次回はサブサイトのコピーを考えます。
ユーザー情報の抽出-SQL- [技術情報]
今回はユーザー情報の取得について書きます。
SharePointに於いてユーザーに関するテーブルは下記のテーブルです。
・UserInfoテーブル … 過去にサイトコレクションにアクセスしたユーザー
代表的なカラム:tp_siteID,tp_ID,tp_login,tp_title,tp_email
・WebGroups … サイトコレクションに登録されている権限グループ
代表的なカラム:SiteID,WebID,ID,Title
・WebMembersテーブル … サブサイト毎のユーザー一覧
全カラム:WebID,UserID,STSToken
・WebGroupMembershipテーブル … サブサイトとユーザーと権限グループを紐つける
全カラム:WebID,GroupID,MemberID
UserInfoテーブルはSiteIDしか情報を持っていないため、他のテーブルと関連づけて抽出しないとサブサイト(WebID)毎には抽出できないようになっています。
上記のテーブルを組み合わせることによりいろいろな情報を取得することが可能です。
下記に私が運用で使用しているSQLを2つほど例として記載しておきます。
サイトユーザーの取得SQL
SELECT DISTINCT u.tp_Login FROM dbo.UserInfo as u
LEFT JOIN WebMembers as wm on u.tp_id = wm.UserID
INNER JOIN WebGroupMemberShip as wgms on wgms.MemberID = u.tp_id
INNER JOIN WebGroups as wg on wgms.WebID = wg.WebID
and wgms.GroupID = wg.ID
WHERE u.tp_Deleted = 0
アクセス権の取得SQL
SELECT w.FullUrl,u.tp_Login,wg.Title from UserInfo as u
LEFT JOIN WebMembers as wm on u.tp_id = wm.UserID
LEFT JOIN Webs as w on u.tp_SiteID = w.SiteID
LEFT JOIN WebGroupMemberShip as wgms on w.ID = wgms.WebID
and u.tp_id = wgms.MemberID
LEFT join WebGroups as wg on wgms.WebID = wg.WebID
and wgms.GroupID = wg.ID
WHERE wgms.MemberID is not NULL
GROUP BY w.FullUrl,u.tp_Login,wg.Title
ORDER BY FullUrl
「サイトとワークスペースの管理」が開けない [技術情報]
「サイトとワークスペースの管理」が表示できなくなった事象の対処法について記載する。
SPS2003でページの表示エラーやサイトの削除エラーが発生する場合は、よくデータベース上に迷子データが残ってしまっている場合が多い。
先日、ユーザー環境にて「サイトとワークスペースの管理」ページを開く際にエラーが発生するとの連絡があり対処を実施した。まず最初に、直前の操作を確認したところ、サブサイトを「サイトの削除」で削除したとのこと。
そこでWebsテーブルのfullurlを削除したサイトURLでLike検索したところ、やはり消したはずのデータが残ってしまっていた。残っていたデータのWebIDをキーに全テーブルを調査し、2つのテーブルに残存していたデータをDELETEしたら無事に「サイトとワークスペースの管理」画面が表示できるようになった。
ここからは想像だが、サイトの削除を実行した際、本来ならデータベースの各テーブルに存在する該当サイトに紐つくデータを削除するが、何らかの原因により途中で止まってしまうことがあるのではないか?Websに消したはずのデータが残ってしまっているため「サイトとワークスペースの管理」を表示する際にWebsに登録されているサイト情報を見に行ってデータが中途半端になっているためエラーになるのではないかと思われる。
SharePointの運用をやっていて思うのだがもう少しエラーログを充実させてほしい。ユーザーからはSharePointでエラーが発生したと画面キャプチャーを送ってきたりするが、ほとんどのエラー画面が同じエラーメッセージのためエラーの判別と対応が非常に難しい。
MOSS2007でも状況は変わらないんだろうなぁ。
WSS3.0 コンテンツDB -(2)- AllDocVersions [技術情報]
今回はドキュメントのバージョン管理で使用するDocVersionsテーブルの差異について調べます。
【DocVersionsとAllDocVersions】
× → CheckinComment
× → Level
× → DraftOwnerId
× → DeleteTransactionId
上記の4カラムが追加になっているようです。
主キー項目(Siteid,Id,Version)はWSS2.0と同じなのでテーブル名は変わっていますが
格納されるデータは変わらないのではないかと推測されます。
WSS3.0 コンテンツDB -(1)- 環境構築~AllDocs構造 [技術情報]
やっとWSS3.0のテスト環境が構築できました。
ReadMeも見ずにテスト的に作ったからちょっと不安だけど、とりあえずサイトの作成ができたから大丈夫そう。でもまだメールとかは設定は完了してないけど。。。まぁ、この辺はkunitakaさんのページとかを参考にあとで修正しましょう。
まず、第一感想。重いっ!!確実にSPS2003より重くなってる。会社のMOSS2007への移行プロジェクトが心配になってきた。無事移行できてもユーザーが納得するかなぁ?
見た目はかなりよくなってるね。しかし、ちょっとした画面の遷移でも時間がかかるのは見た目を強化したせいだろうか?
実は会社ではSQLServer2000のストアドを駆使していろいろな統計情報を取得したり更新処理を保管したりしています。今回WSS3.0ではゴミ箱機能が装備されたこともありかなりのストアドを修正する必要性があるのではないかと考えています。
SPS2003のデータベース構造は以前書いたことがありますがWSS3.0はどうなっているのでしょうか?早速、気になっていたデータベースの中身を確認してみた。
【主なテーブル】
ComMd → ComMd
Docs → AllDocs ?
DocVersions → AllDocVersions ?
Lists → AllLists ?
Personalization → Personalization
SiteGroups → Groups ?
Sites → Sites
UserData → AllUserData ?
UserInfo → UserInfo
WebGroupMemberShip → GroupMemberShip ?
WebGroups → Groups ?
WebMembers → WebMembers
WebParts → Webparts
Webs → Webs
ゲゲッ!?テーブルの数が多い。。。しかもテーブルの名前自体が変わってる。ストアドは全滅かも。
つぎにDocsのカラムについて確認する。
【DocsとAllDocsの差異】
Docsから削除されたカラム
CheckoutSize → ×
Content → ×
CheckoutContent → ×
AllDocsに追加されたカラム
× → ListDateDirty
× → ProgId
× → DeleteTransactionId (PK) 主キーが追加されているということはゴミ箱対応かな?
× → SetupPathVersion
× → SetupPathUser
× → UnVersionedMetaInfo
× → UnVersionedMetaInfoSize
× → UnVersionedMetaInfoVersion
× → WelcomePageUrl
× → WelcomePageParameters
× → IsCurrentVersion
× → Level
× → CheckinComment
× → AuditFlags
× → InheritAuditFlags
× → DraftOwnerId
× → UIVersionString
× → ParentId
× → HasStream
× → ScopeId
× → BuildDependencySet
× → ParentVersion
× → ParentVersionString
× → TransformerId
× → ParentLeafName
× → IsCheckoutToLocal
× → CtoOffset
× → ExtensionForFile
× → ItemChildCount
× → FolderChildCount
うーん、単純に考えてもデータカラムが増えているということは書き込みにも時間がかかるわけで、重い理由がなんとなくわかった気がする。
ゴミ箱実装失敗 [技術情報]
MOSS2007にはゴミ箱機能が実装されているようですね。
うーん、似たような機能を簡単にSPS2003に実装できないかと考えてチャレンジしました。
対象としてドキュメントライブラリに格納した文書に限定したらSQLServerのTriggerでなんとかならないかと思ってやってみたのですが。。。駄目でした。
ドキュメントはデータベースのDocsテーブル、Listsテーブル、UserDataテーブルに情報が格納されます。だからDELETE Triggerで同一構造のゴミ箱テーブルに待避するトリガーを作成して実行してみたのですが、transact-SQLの制限でImage型を使えないようでエラーになってしまいました。
やっぱりドキュメントライブラリイベントをハンドリングするWebパーツを作らないと駄目みたいですね。
時間があったらチャレンジします。
そのまえにMOSS2007に移行しろって言われそう(汗)
リストを別サイトに移行する -(2)- [技術情報]
リストを別のサイトに移行する -(1)- で記載したように標準のリストテンプレートでコンテンツを含んだリストテンプレートを作成できないリストが存在します。
下記の例は案件テンプレートを使用したリストです。
なぜだかわかりませんが、このリストをテンプレートとして保存する際に「コンテンツを含む」チェックボックスが選択できないようになっています。
これを回避する方法としてはサーバー側のテンプレート設定XMLファイルを修正することにより回避することができます。ただし、修正前のテンプレートで作成してしまったリストには反映されません。
下記に方法を記載します。
テンプレート設定XMLファイル名:ONET.XML
サーバー側保存場所:C:\Program Files\Common Files\Microsoft Shared\web server extentions\60\TEMPLATE\1041\STS\XML
ONET.XMLのissueに関する記述の中にある DontSaveInTemplate = "TRUE" を削除して保存します。
<ListTemplate Name="issue" DisplayName="Issues" Type="1100" BaseType="5" DontSaveInTemplate="TRUE" OnQuickLaunch="TRUE" SecurityBits="11" Description="Create an issues list when you want to manage a set of issues or problems. You can assign, prioritize, and follow the progress of issues from start to finish." Image="/_layouts/images/itissue.gif">
</ListTemplate>
その後、IISリセットを実行し、案件リストテンプレートを使用してanken3リストを作成します。
この状態でテンプレートとして保存します。このとき「コンテンツを含む」がチェックできるようになっています。
テンプレート保存後に別サイトで「案件3」を指定してリストを作成します。
すると、データも含めて別サイトに案件リストが移行できました。
なぜ案件リストがコンテンツを含めたリストテンプレート作成に対応していないのかは不明ですが上記の手順によりコンテンツを含めたリストテンプレート作成に対応することができます。
本手順はサーバー側の設定ファイルを修正しますのでバックアップを必ずとってから作業するようにしてください。
リストを別サイトへ移行する -(1)- [技術情報]
SharePointの世界においてリストは非常に重宝される情報蓄積手段です。
リストには標準でいくつかのテンプレートが用意されています。リンク、お知らせ、連絡先、イベント、ToDo、案件など。それとは別にユーザーが列を自由に作成できるカスタムリスト。
サイトが肥大化したりした場合にサブサイトを作成し、ここに今まで蓄積されたデータを移動したいと考えたとき有効な手段の一つとしてリストをテンプレート化するときにコンテンツを含むをチェックしてサブサイトに移す方法があります。
リストテンプレート化するにはリストの「設定と列の変更」から「リストをテンプレートとして保存」を選択します。
その後でコンテンツを含むにチェックを入れて保存します。
その後、移行したいサブサイトでリストを作成する際に「氏名リスト」テンプレートを選択します。
するとデータも含めた移行が完了します。
ただ、上記方法で移行できないリストも存在します。
次回は標準ではデータを含んだリストテンプレートが作成できない標準リストテンプレートについて書きたいと思います。
使用容量計算 [技術情報]
ユーザーがWebUI上で容量の確認をしようと思うと「サイト コレクションの利用状況の概要」でサイトコレクション単位で確認することができます。ただし、サイトコレクション単位でしかデータは存在していません。
しかもそのデータは集計値ではなく累積値になっています。つまり、サイトコレクションにファイルが追加・削除されるたびに加算・減算を行っているようです。
Sitesテーブルに存在する下記のカラムがそう。
・DISKUSED … ディスク使用量
そのほかにもこんなカラムがあります。
・DISKQUOTA … クォータ設定値
・DISKWARNING … 警告メール送信値
それじゃぁつまらないので、できる限り正確なサブサイト単位の容量を計算してみたいと思います。
まず、容量は2つに分かれています。1つがサイトコレクション単位の容量、もう一つがサブサイト単位の容量です。
サイト単位 … WebPartsテーブル
サブサイト単位 … Docs, DocVersions, ComMd, Personalization, Lists, UserData
まず、サイト単位の情報を取得します。
-- WebParts
SELECT Sites.FullUrl AS SiteUrl,Sites.id AS SiteId,Sites.DiskUsed AS DiskUsed,
Sites.DiskQuota AS DiskQuota,Sites.DiskWarning AS DiskWarning,
Sum(ISNULL(WebParts.tp_Size,0)) AS WebPartsSize FROM Sites
LEFT OUTER JOIN WebParts ON Sites.id = WebParts.tp_SiteId
GROUP BY Sites.FullUrl,Sites.id,DiskUsed,DiskQuota,DiskWarning
ORDER BY Sites.FullUrl
次にサブサイト単位にDocsの情報を集計します。
-- Docs
SELECT Sites.FullUrl AS SiteUrl, Webs.FullUrl AS SubSiteUrl, Sites.Id AS SiteId,
Webs.Id AS WebId, ISNULL(SUM(CAST(ISNULL(Docs.Size, 0) AS BIGINT) +
CAST(ISNULL(Docs.CheckoutSize, 0) AS BIGINT)+
CAST(ISNULL(Docs.MetaInfoSize, 0) AS BIGINT)),0) AS DocSize
FROM Sites
LEFT OUTER JOIN Webs ON Sites.Id = Webs.SiteID
LEFT OUTER JOIN Docs ON Sites.Id = Docs.SiteId and Webs.id = Docs.webid
GROUP BY Sites.FullUrl,Webs.FullUrl,Sites.Id,Webs.Id
ORDER BY Sites.FUllUrl,Webs.FullUrl
次にDocVersions,ComMd,Personalizationを集計します。
-- DocVersions,ComMd,Personalizations,
SELECT Sites.FullUrl AS SiteUrl, Webs.FullUrl AS SubSiteUrl, Sites.Id AS SiteId,
Webs.Id AS WebId, SUM(CAST((ISNULL(DocVersions.Size, 0)
+ ISNULL(DocVersions.MetaInfoSize, 0)) AS BIGINT)) AS DocVerSize,
SUM(ISNULL(CONVERT(BIGINT,ComMd.[size]),0)) AS ComMdSize,
SUM(ISNULL(CONVERT(BIGINT,Personalization.[tp_size]),0)) AS PersonalSize FROM Sites
LEFT OUTER JOIN Webs ON Sites.Id = Webs.SiteID
LEFT OUTER JOIN Docs ON Sites.Id = Docs.SiteId and Webs.id = Docs.webid
LEFT OUTER JOIN DocVersions ON Sites.id = Docversions.siteid and Docs.id = DocVersions.id
LEFT OUTER JOIN ComMd ON Sites.id = ComMd.siteid and Docs.id = ComMd.docid
LEFT OUTER JOIN Personalization ON Sites.id = Personalization.tp_siteid
and Docs.id = Personalization.tp_pageurlid
GROUP BY Sites.FullUrl,Webs.FullUrl,Sites.Id,Webs.Id ORDER BY Sites.FUllUrl,Webs.FullUrl
次にリストデータの集計をします。
-- Lists,UserData
SELECT Sites.FullUrl AS SiteUrl, Webs.FullUrl AS SubSiteUrl, Sites.Id AS SiteId,
Webs.Id AS WebId, SUM(ISNULL(CONVERT(BIGINT,UserData.[tp_Size]), 0)) AS UserDataSize
FROM Sites
LEFT OUTER JOIN Webs ON Sites.Id = Webs.SiteID
LEFT OUTER JOIN Lists ON Webs.Id = Lists.tp_WebId
LEFT OUTER JOIN UserData ON Sites.id = UserData.tp_siteid and Lists.tp_id = UserData.tp_ListId
GROUP BY Sites.FullUrl,Webs.FullUrl,Sites.Id,Webs.Id
ORDER BY Sites.FUllUrl,Webs.FullUrl
上記のDocs, DocVersions, ComMD, Personalization, UserDataの集計値を加算した値に最初で算出したWebPartsの集計値を加算します。
ただし、正式に発表された情報ではないのであくまでも目安としてつかってください。
アップロード時のファイル名自動生成 for SPS2003 [技術情報]
今日はドキュメントライブラリへのアップロード時のファイル名変更について記載します。MOSS2007ではいろいろな方法が提案されていますがもう少し違ったアプローチでSPS2003で実現してみましょう。
今回はデータベーストリガーを使用したアプローチを紹介します。データベーストリガーはデータベースに対してInsert,Delete,Update文発行前後におのおの動作するように指定が可能になっています。これを利用します。
たとえば、「障害レポート」ドキュメントライブラリを作成し、障害発生時にアップロードする運用を考えます。
障害レポートのひな形は「障害報告.xls」。これはひな形をドキュメントライブラリに登録しておきトップページにリンクを張っておきます。
障害発生時に運用担当者がリンクから「障害報告.xls」をローカルPCにダウンロードし、これを記入した後で「障害レポート」ドキュメントライブラリにアップロードします。
この運用の場合、下記の要件が考えられます。
・上書きしないようにファイル名を変えてアップロードする。
・定められた命名規則に則って変更してアップロードする。
・「障害レポート」ドキュメントライブラリのみファイル名を自動生成させる。
これを自動化する運用を考えます。
SQLServerの場合はInsert,Delete,Update文発行時にINSERTED,DELETEDテーブルに更新する値が保持されます。
またドキュメントライブラリにアップロードされたドキュメントはDocsテーブルに保存されます。Docsテーブルには下記の使えそうなColumnが用意されています。
ID … ドキュメントごとにユニークなID
LEAFNAME … ファイル名
DOCLIBROWID … ドキュメントライブラリごとにユニークなID
DIRNAME … ファイルのURL
今回の要件の場合はドキュメントライブラリにアップロードされたタイミングで実行されればいいのでDocsテーブルにInsert文が発行された後にトリガーが動くようにトリガーを張ります。
トリガーの中ではDocsテーブルに対してInsertされたデータに対して下記の条件をチェックします。
・ドキュメントライブラリに登録された文書か?
・「障害レポート」ドキュメントライブラリに対してInsertされたか?
上記条件を満たした場合、ファイル名の前に"[TR-00000x]"のヘッダーを追加するようにします。
【トリガー】
--------------------------------------------------------------------------
/*
ドキュメントライブラリ通番付加ストアドプロシージャ
前提
・サーバーオプション 「nested trigger」 が1になっていること
・WebURLを指定すること
*/
CREATE TRIGGER trigger_DoclibNumbering ON Docs
AFTER INSERT AS
BEGIN
DECLARE @ID UNIQUEIDENTIFIER
DECLARE @LeafName NVARCHAR(255)
DECLARE @DocLibRowID INT
DECLARE @LenRowID INT
-- 更新前データの取得
SELECT
@ID = Id,
@LeafName = LeafName,
@DocLibRowID = DocLibRowID,
@LenRowID = LEN(DocLibRowID)
FROM
INSERTED
WHERE
DIRNAME = 'sites/test/TroubleReport/TR_Report'
-- 見つからない場合は何もしない
IF @LEAFNAME IS NULL
BEGIN
-- 対象外データ
RETURN
END
-- ファイル名の更新
UPDATE Docs
SET LeafName = '[TR-' + STUFF('000000',6 - @LenRowID,@LenRowID + 1,CAST(@doclibrowid AS NVARCHAR) ) + ']' + @LEAFNAME
WHERE Id = @ID
END
--------------------------------------------------------------------------
では、実際に運用してみましょう
まずは障害管理.xlsをダウンロードし、障害内容を記入します。
そして、ドキュメントライブラリにアップロードします。
すると、自動的にファイル名の頭にヘッダー部をつけて登録してくれました。
もうひとつ
今度はエクスプローラビューにドラッグ&ドロップしてみましょう。すると。。。
あら、失敗か? 試しにすべてのドキュメントビューに戻すと。
おおっ、うまく動いているようです。
編集もすべてのビューからは問題なくできます。ただ、エクスプローラビューはそのままだとファイル名が違うので開けません。そこでいったんブラウザを閉じて再度エクスプローラビューにするとファイル名がきちんと表示されて編集が可能になります。
という具合にデータベーストリガーを利用することにより比較的簡単に自動採番を実施できます。
今回は自動採番機能にDocLibIDを使用しましたが日付を使用したり、採番用のストアドを作成する
ことによりいろいろな使い方ができると思います。