AEM開発者の皆様、お疲れ様です。大和株式会社の狩野です。
タイトルが有名な著書のオマージュですが、なんとAEMでもその「退屈なこと」をPythonにやらせることができちゃうんです。 今回は、その方法についてお教えいたします。
前提条件
・Python 3
・AEM6.5
ページを作成する
どういう方法で…というのを説明しているよりは、コード自体を見てもらったほうが早いと思うので、早速コードをお見せします。 Pythonスクリプトで自動でページを作成してもらうようなコードは以下のようになります。
ソースコード
# coding: utf-8 import requests def main() : AEM_HOST = 'http://localhost:4502' USER = 'admin' PASSWORD = 'admin' TEMPLATE_PATH = '/conf/we-retail/settings/wcm/templates/product-page' auth = (USER, PASSWORD) parent_path = '/content/we-retail/us/en' page_name = 'special-product' if page_exists(AEM_HOST, auth, parent_path + '/' + page_name) : print(f'Page {page_name} already exists. Skipping creation.') return creation_data = { "cmd": "createPage", "parentPath": parent_path, "title": page_name, "label": page_name, "template": TEMPLATE_PATH } response = requests.post(f"{AEM_HOST}/bin/wcmcommand", auth=auth, data=creation_data) if response.status_code == 200: print(f"Page {page_name} created successfully.") else: print(f"Failed to create page {page_name}: {response.status_code} - {response.text}") def page_exists(AEM_HOST, auth, page_path): """対象のページが存在するか確認する関数""" url = f"{AEM_HOST}{page_path}.html" print(url) response = requests.get(url, auth=auth) return response.status_code == 200 if __name__ == '__main__' : main()
解説
if page_exists(AEM_HOST, auth, parent_path + '/' + page_name) : print(f'Page {page_name} already exists. Skipping creation.') return
AEMで二重にページを作ろうとすると、「special-product0」といったようなページが作られるので、それを抑制しております。
creation_data = { "cmd": "createPage", "parentPath": parent_path, "title": page_name, "label": page_name, "template": TEMPLATE_PATH } response = requests.post(f"{AEM_HOST}/bin/wcmcommand", auth=auth, data=creation_data)
変数 creation_data
には、作成するページの情報をdict型で格納します。
各項目の意味については以下になります。
- cmd: コマンド。今回はページを作成したいので
createPage
を設定しています。 - parentPath: 作りたいページの親ページのパス。今回は
/content/we-retail/us/en/special-product
というページを作りたいので、/content/we-retail/us/en
を設定しています。 - title: jcr:titleプロパティに入る値
- label: ノード名
- template: テンプレートパス
勘の良い方は気づいたと思いますが、AEM での cURL の使用 をコマンドラインではなくPythonで行っているだけなんですね。
先の例のように1ページ作るだけであれば、手でページを作成するなり、紹介したCurlコマンドでやればいいだけの話ですが、次以降の話でPythonで行う強みが見えてきます。
複数ページを作成する
ソースコード
special-product wonderful-product super-product extra-product
# coding: utf-8 import requests def main() : AEM_HOST = 'http://localhost:4502' USER = 'admin' PASSWORD = 'admin' TEMPLATE_PATH = '/conf/we-retail/settings/wcm/templates/product-page' auth = (USER, PASSWORD) PARENT_PATH = '/content/we-retail/us/en/products' with open('page_names.txt', mode='r', encoding='utf_8') as f : page_names = [l.strip() for l in f.readlines()] for page_name in page_names : if page_exists(AEM_HOST, auth, PARENT_PATH + '/' + page_name) : print(f'Page {page_name} already exists. Skipping creation.') continue creation_data = { "cmd": "createPage", "parentPath": PARENT_PATH, "title": page_name, "label": page_name, "template": TEMPLATE_PATH } response = requests.post(f"{AEM_HOST}/bin/wcmcommand", auth=auth, data=creation_data) if response.status_code == 200: print(f"Page {page_name} created successfully.") else: print(f"Failed to create page {page_name}: {response.status_code} - {response.text}") def page_exists(AEM_HOST, auth, page_path): """対象のページが存在するか確認する関数""" url = f"{AEM_HOST}{page_path}.html" print(url) response = requests.get(url, auth=auth) return response.status_code == 200 if __name__ == '__main__' : main()
解説
1つ前の処理をループにしただけですね。 今回は、txtファイルにまとめた名前のページを連続で作成します。
先の1ページだけ作る処理とは違い、これならスクリプトで自動化する意味もあるでしょう。 今は、中身がないページを作っただけですが、中身のAuthoringについては Data Importer などを使ってnodeに直接プロパティを書き込む等で自動化が可能ですし、この後の「ページプロパティを書き換える」を応用することによっても可能です。
画像をDAMに格納する
ページ移行などで、移行元サーバにある画像をAEMのDAM Assetsに格納することも可能です。
今回の例では、 AEM関連サービス - WCMサービス|大和株式会社 のトップにある画像をローカル /content/dam/we-retail/en/products
にアップロードします。
ソースコード
import requests from io import BytesIO AEM_HOST = "http://localhost:4502" AEM_USER = "admin" AEM_PASSWORD = "admin" folder_path = "/api/assets/we-retail/en/products" asset_name = "YAMATO-AEM-capabilities-diagram.png" upload_url = f"{AEM_HOST}{folder_path}/*" image_url = "https://wcm.yamato-ltd.com/wp/wp-content/uploads/2022/03/YAMATO-AEM-Capabilities-diagram.png" download_response = requests.get(image_url) if download_response.status_code != 200: print(f"Failed to download image: {download_response.status_code}") return image_data = BytesIO(download_response.content) files = { "name": (None, asset_name), "file": (asset_name, image_data, "image/jpg") } upload_response = requests.post(upload_url, auth=(AEM_USER, AEM_PASSWORD), files=files) if upload_response.status_code in [200, 201]: print(f"Asset '{asset_name}' created successfully.") else: print(f"Failed to create asset: {upload_response.status_code} - {upload_response.text}")
解説
Assets HTTP API を参考にいたしました。
注意すべき点としては、アップロード先のフォルダパスから /content/dam
を除くことでしょうか。
自分は最初に使おうとしたときにここで2時間ほどを溶かしました……。
なお今回、前提条件を「AEM6.5」としましたが、Cloud版AEMではAssets HTTP APIによるAssetのアップロードをサポートしていないためです。
詳しくは Adobe Experience Manager Assets HTTP API を使用したデジタルアセットの管理 や Adobe Experience Manager Assets デベロッパー向けの使用例、API、参考資料を参照してください。
ページプロパティを書き換える
一括でページプロパティを書き換えることもできます。 こちらはAEM Groovy consoleなどで似たこともできますが、Pythonのほうが書きやすいという場合はこちらで行うこともできます。
ソースコード
# coding: utf-8 import requests def main() : AEM_HOST = 'http://localhost:4502' USER = 'admin' PASSWORD = 'admin' auth = (USER, PASSWORD) page_path = "/content/we-retail/us/en/special-product" properties = { "jcr:description": "This is page of special product !", "jcr:title": "special-product | We-retail" } headers = { "Content-Type": "application/x-www-form-urlencoded" } prop_response = requests.post( f"{AEM_HOST}{page_path}/jcr:content", auth=auth, data=properties, headers=headers ) if prop_response.status_code in [200, 201]: print(f"Properties updated for {page_path}.") else: print(f"Failed to update properties for {page_path}: {prop_response.status_code} - {prop_response.text}") if __name__ == '__main__' : main()
解説
今回は、 jcr:content
ノードに対してリクエストを送出することによって、ページプロパティを更新していますが、やり方によってはページに置いたコンポーネントのプロパティを書き換えることも可能となります。
その場合は、配置したコンポーネントのノード構造を把握しておく必要があります。
最後に
自分は、以前お客様よりAEM以外のCMSで作られたページをAEMに大量に移行したいという要望があり、それを実現するために色々調べたのを皆様に共有させていただきました。
今回は自分が慣れているなどの理由でPythonで書きましたが、その気になればシェルスクリプトなどでも可能だと思います!