S1アプリ側で画面遷移するときはsforce.one.navigateToURL()を利用します。
このnavigateToURLはVFページから別のVFページに遷移するときに利用できたっけ...と思って試してみました。
ちゃんと動きました。
↓
↓
↓
S1アプリ側で画面遷移するときはsforce.one.navigateToURL()を利用します。
このnavigateToURLはVFページから別のVFページに遷移するときに利用できたっけ...と思って試してみました。
ちゃんと動きました。
↓
↓
↓
Spring'16で新しく追加されたforceChatter:fullFeedタグを試してみました。少し特殊なタグでLightning Outでのみ利用できるみたいです。また、現時点ではベータ版となっているみたいです。
サンプルコードです。
まだベータ版のためかイマイチな部分もありましたが、次のようなことができました。
だいたいこんな感じです。Google Chromeで確認してみたのですが、一部クリックしても動作しないボタンがありました。共有ボタンです。
一番重要なところじゃ...と思ったのですが、まだベータ版ということもあるので今はどんな感じか確認できればいいのかなと思います。もしかするとVisualforce以外から呼び出せば正常に動作するかもしれません。
マップの上に経路とかを表示できるD3 Floor Planの使い方について調べてみました。
参考になったサイトは以下の3つです。
実際に動くサンプルコードが見当たらなかったので調査ついでにつくってみました。
サンプルコード内で使用しているデータはJSON形式で読み込みますが、そのままだと次のようにエラーになってしまいます。
上記エラーを解決するためにNode.jsを利用しました。GitHubからサンプルファイル一式をダウンロードした後に次のコマンドでNodeの実行準備を行います。
$ npm init $ npm install express --save $ npm run start
これでlocalhost:8080にアクセスして動作確認ができると思います。
利用時に必要なJSの読み込み内容は以下のとおりです。Zipファイルにはいろいろなファイルが用意されていますが、必要なのはD3.jsの読み込みとd3.floorplan.min.jsの読み込みだけでした。
<!-- D3.js --> <script src="http://d3js.org/d3.v3.min.js"></script> <!-- D3.js Floor Plan --> <link rel="stylesheet" type="text/css" href="lib/d3-floorplan/d3.floorplan.css" /> <script src="lib/d3-floorplan/d3.floorplan.min.js"></script>
実際の処理は『d3-floorplan-sample/public/javascripts/sample.js』に記載してあります。基本利用できるのは以下のオプションです。
次のように初期化します。
var imagelayer = d3.floorplan.imagelayer(); var heatmap = d3.floorplan.heatmap(); var pathplot = d3.floorplan.pathplot(); var overlays = d3.floorplan.overlays().editMode(true);
ベースになる図の画像は以下のように設定します。(URLに画像のパスを設定)
//ベースとなる画像データの読み込み mapdata[imagelayer.id()] = [{ url: 'images/demo.jpg', x: 0, y: 0, height: 33.79, width: 50.0 }];
ヒートマップや経路などの設定は次のように行います。
//レイヤーの追加 map.addLayer(imagelayer) .addLayer(heatmap) .addLayer(pathplot) .addLayer(overlays); //ポリゴン、ヒートマップ、パスデータの読み込み d3.json("data/data.json", function(data) { mapdata[heatmap.id()] = data.heatmap; mapdata[overlays.id()] = data.overlays; mapdata[pathplot.id()] = data.pathplot; //フロアマップ表示 d3.select("#demo").append("svg") .attr("width", w) .attr("height", h) .datum(mapdata) .call(map); });
『d3.json("data/data.json" ....(略)』の部分でデモ用のデータを読み込んでいます。データはJSON形式になります。基本的にX座標とY座標の値がわかればいいみたいです。以下デモ用データの一部です。(詳細は『d3-floorplan-sample/public/data/data.json』)
"vectorfield": { "binSize": 3, "units": "ft/s", "map": [ {"x": 18, "y": 21, "value": {"x": 4, "y": 3}}, {"x": 21, "y": 21, "value": {"x": 3, "y": 3}}, {"x": 18, "y": 24, "value": {"x": 1, "y": 2}}, {"x": 21, "y": 24, "value": {"x": -3, "y": 4}}, {"x": 24, "y": 24, "value": {"x": -4, "y": 1}}] }, "pathplot": [{ "id": "flt-1", "classes": "planned", "points": [{"x": 23.8, "y": 30.6},{"x": 19.5, "y": 25.7},{"x": 14.5, "y": 25.7},{"x": 13.2, "y": 12.3}] }] }
サンプルは公式サイトと同じようにつくっていますが、後はベースになる画像を用意してX座標とY座標を指定していく感じで利用できると思います。
最後にGitHub上にHerokuボタンも用意してみました。ボタンをクリックするだけでHeroku上で動作確認できると思います。
Salesforce標準機能のひとつ、承認申請の履歴情報を取得できるSOQLクエリの実行方法についてです。
承認履歴の情報は以下のオブジェクトに格納されています。
このうち、ProcessInstanceWorkitemとProcessInstanceStepの項目はProcessInstanceHistoryでまとめて取得できるみたいです。
ProcessInstanceHistoryの情報はProcessInstanceオブジェクトのサブクエリでまとめて取得できます。
SELECT Id, (SELECT Id, StepStatus, Comments FROM StepsAndWorkitems) FROM ProcessInstance
ProcessInstanceNodeオブジェクトには日付情報などより詳細な情報が登録されています。このオブジェクトはサブクエリなどで取得できません。ProcessInstanceオブジェクトのIDなどを持っているのでそちらで紐付けます。
サンプルクエリです。
gist.github.com
これで一通りの必要な情報を取得できると思います。
ProcessInstanceHistoryの『OriginalActorId』は割当先、『ActorId』は承認者となっているみたいです。
ユーザIDがセットされていますが、クエリでユーザ名を紐つけることはできないみたいで、別にユーザ情報をMap変数か何かに準備して紐つける必要がありそうです。
システム管理者権限で強制的に再度承認申請を行ったりして次のようになっている場合は、ProcessInstanceレコードが2件存在している状態です。(確認していませんが却下→承認申請も同様だと思います。)
承認履歴とSOQLクエリの情報はこんな感じです。
承認履歴とSOQLクエリということで、こういうブログを書きました。
Twitterでより良い方法について教えてもらったので、こちらで追記しようと思います。
承認ステップの日付情報を取得するためにProcessInstanceNodeオブジェクトの情報を取得しましたが、この日付部分はステップレコードの作成日の値と同じものでした。
そのため、次のクエリだけで必要な情報は取得できそうです。
public List<ProcessInstance> getProcessInstances(String targetObjectId) { return [ SELECT Id ,CompletedDate ,LastActorId ,ProcessDefinitionId ,Status ,TargetObjectId ,( SELECT Id ,ActorId ,Comments ,IsPending ,OriginalActorId ,ProcessInstanceId ,ProcessNodeId ,StepStatus ,TargetObjectId ,CreatedDate FROM StepsAndWorkitems ORDER BY CreatedDate DESC ) FROM ProcessInstance WHERE TargetObjectId =: targetObjectId ORDER BY CreatedDate DESC LIMIT 50 ]; }
これを踏まえて簡単なサンプルをつくって確認してみました。ステップ名の表示とか細かい部分が不要な場合はこれで問題なさそうです。
サンプルコードです。
もうひとつ、割り当て先や承認者の項目(OriginalActorIdとActorId)はなぜクエリでユーザ名まで取得できないのかなと思っていたのですが、これについても教えてもらいました。承認者の割り当てではユーザだけでなくキューも指定することが可能です。
承認申請でキューを利用する場合は、キューオブジェクトの方にクエリを実行する必要があるので、状況に応じて考慮しておく必要があるみたいです。
ちなみにキューの名前はグループオブジェクトから取得できます。
SELECT Id,QueueId,SobjectType FROM QueueSobject
SELECT Id,Name,Type FROM Group WHERE Type= 'Queue'
承認履歴のクエリを実行するときですが、ポータルユーザやCommunityユーザではアクセスできないオブジェクトがあるので注意が必要です。
承認履歴で考慮が必要なのはこんな感じでした。
StepStatus項目を取得すると英語で取得されます。
標準画面では日本語なのでなんでだろうと思っていたのですが、選択リスト項目でトランスレーションワークベンチで日本語変換されているだけでした。
なのでtoLabel()を利用することで日本語に変換して取得できます。
取得後にIF処理判定したりする必要はありませんでした。
何かと便利なoutputFieldですが、ProcessHistoryオブジェクトはちょっと特殊な感じだし利用できないかなと自分でラッパークラスして対応しようと思いました。
・・・普通にoutputFieldで対応できたのでそっちの方がシンプルになりました。
特殊なオブジェクトの場合でoutputFieldがサポートされていないケースというのも見たことがあったのですが、承認履歴は普通にサポートされていたのでそちらで対応する方が簡単です。ユーザorキューがセットされるActorId項目も簡単に表示できて便利です。
GitHubのサンプルコードも直しておきました。
また、承認履歴情報は汎用的に利用できると思うのでVisualforceコンポーネントにしておくと良さそうです。教えて貰ったコードですが、こんな感じです。
<apex:page standardController="Account"> <c:ApprovalHistoryComponent targetObjectId="{!Account.Id}"/> </apex:page>
<apex:component controller="ApprovalHistoryController"> <apex:attribute name="targetObjectId" type="Id" required="true" description="ProcessInstance.TargetObjectId" assignTo="{!targetObjectId0}"/> <apex:pageBlock mode="detail" title="承認履歴"> <apex:pageBlockTable value="{!history}" var="e"> <apex:column value="{!e.ProcessNode.Name}" headerValue="ステップ"/> <apex:column value="{!e.CreatedDate}" headerValue="日付"/> <apex:column value="{!e.StepStatus}"/> <apex:column value="{!e.OriginalActorId}"/> <apex:column value="{!e.ActorId}"/> <apex:column value="{!e.Comments}"/> </apex:pageBlockTable> </apex:pageBlock> </apex:component>
public class ApprovalHistoryController { public Id targetObjectId0 { get; set; } public ProcessInstanceHistory[] history { get { ProcessInstanceHistory[] xs = new ProcessInstanceHistory[] {}; for (ProcessInstance e : [ select Id, ( select Id, ProcessNode.Name, CreatedDate, StepStatus, OriginalActorId, ActorId, Comments from StepsAndWorkitems order by IsPending desc, CreatedDate desc, Id desc ) from ProcessInstance where TargetObjectId = :targetObjectId0 order by CreatedDate desc, Id desc ]) { xs.addAll(e.StepsAndWorkitems); } return xs; } } }
Visualforceとコンテンツファイルのダウンロードリンクについてです。通常のVFページでは次のような感じでJS実装するとコンテンツファイルをダウンロードできます。
<a onclick="return fileDownload('068A0000001RNA8');">ファイルダウンロード</a>
// コンテンツダウンロード function fileDownload(prmId) { location.href = '/sfc/servlet.shepherd/version/download/' + prmId; return false; }
Force.comサイトでも同じようにいけるかなと思ったのですが、次のようになってしまいダメでした。(未ログイン状態で確認)
外部公開されていないファイルを未ログイン状態で参照することはできないのでよく考えたら当たり前か...という感じなのでですが、それならコンテンツ配信したものはどうなるんだろうとそれも試してみました。
コンテンツ配信のURLの場合は、こんな感じでダウンロードページが表示されました。
ざっくり確認ですが、サイトでコンテンツのファイルにアクセスしたい場合はいろいろ考慮しないといけないことがあるみたいです。
承認履歴の一覧を表示するには、ProcessInstanceHistoryオブジェクトなどを利用することになりますが、このオブジェクト、特別なアクセスルールということで「ポータルユーザとコミュニティユーザはこのオブジェクトにアクセスできません。」となっています。
実際にコミュニティユーザでクエリを実行してみました。
このように『sObject type 'ProcessInstance' is not supported.』となってしまい、承認履歴の情報にアクセスすることはできませんでした。
そもそもコミュニティユーザでは承認申請自体利用できなかったりして..と思い、その部分についても確認してみました。
結果、コミュニティユーザでもApexから承認申請処理を正常に実行することができました。ということで承認履歴関係のオブジェクトを参照できないだけで、承認申請自体は問題なく利用できました。
一般ユーザと同じ画面を使いまわそうとしてエラーになる。そんな落とし穴にハマる可能性も考えられるのでコミュニティユーザ向けの機能開発を行う場合はこの辺のルールを意識しておいた方がよさそうです。
ちなみにカスタム項目で承認状況のステータスを登録するための項目を用意してあげれば、同じように承認状況を表示することも可能だと思います。実際にコミュニティユーザで承認履歴情報にアクセスすることになったときはそんな感じで対応できるんじゃないかなと思います。
Salesforce1アプリ用にVisualforceページを開発しようとすると、スクロールがトップに戻される問題に遭遇します。
この問題はiOS版のSalesforce1アプリで発生するみたいです。回避方法がこちらで紹介されていました。
Salesforce1 - page automatically scrolls to top when user taps on a page element on iOS devices only
1) Add the following JavaScript to your page:
<script> (function(){try{var a=navigator.userAgent; if((a.indexOf('Salesforce')!=-1)&&(a.indexOf('iPhone')!=-1||a.indexOf('iPad')!=-1)&&(a.indexOf('OS/8')!=-1||a.indexOf('OS 8')!=-1)&&(a.indexOf('Safari')==-1)){ var s=document.createElement('style'); s.innerHTML="html,html body{overflow: auto;-webkit-overflow-scrolling:touch;}body{position:absolute;left:0;right:0;top:0;bottom:0;}"; document.getElementsByTagName('head')[0].appendChild(s);}}catch(e){}})(); </script>
2) Add the following JavaScript to your page:
<script> var ua=navigator.userAgent; if((ua.indexOf('Salesforce')!=-1)&&(ua.indexOf('iPhone')!=-1||ua.indexOf('iPad')!=-1)&&(ua.indexOf('OS/8')!=-1||ua.indexOf('OS 8')!=-1)&&(ua.indexOf('Safari')==-1)){ function IOS_SCROLL_BOOTSTRAP() { var children = Array.prototype.slice.call(document.body.children), placeholder = document.createElement('section'), fragment = document.createDocumentFragment(), styles, width, height; children.forEach(function(c){fragment.appendChild(c);}); placeholder.appendChild(fragment); styles = [ 'width:100%;', 'height:', (window.screen.height - 42), 'px;', 'position: absolute; overflow: auto; -webkit-overflow-scrolling: touch' ].join(''); placeholder.style.cssText = styles; document.body.appendChild(placeholder); } window.addEventListener('load', function (e) { IOS_SCROLL_BOOTSTRAP(); }); } </script>
判定処理の中に『indexOf('OS/8')』がありますが、これはバージョンを変えて対応する必要があります。除外してもとりあえず動作しました。
1)と2)の二種類が用意されているのですが、どちらかがあればいいのか両方必要なのかちょっとわからなかったのですが、両方入れておいても問題ありませんでした。
これでS1アプリでスクロールがトップに戻る問題を回避することができます。最後にこの方法で対応した場合、AngularJSの『autoscroll="true"』が反映されなくなりました。大きな問題ではないと思いますが、こういった影響があることも意識して置いたほうがよさそうです。
Salesforce1アプリ開発でAngularJsとRemoteActionをつかっていたときに、$rootScope:inprogエラーに遭遇しました。
このエラーは、applyメソッド呼び出しがあり、 更新処理が多重実行されてしまったときに発生するみたいです。
今回のケースでは、inputフィールドに文字が入力されたタイミングでRemoteActionをつかったリアルタイム検索処理をしようとして発生しました。
今回の現象の不思議なところとして、モバイル端末ではなくPC環境で利用した場合はエラーが発生しないということです。(PCからS1モードで確認)
回避方法は見つけられませんでしたが、S1モバイルアプリでのみ発生するみたいなので、このエラー発生時にはエラー表示をせずにそのまま利用できるようにしてしまってもいいのかなと思います。
とりあえずAngularJSをつかってS1アプリを開発する場合、こういった現象があることを意識しておくといいのかなと思います。
RemoteActionの処理を実行時に次のようなエラーに遭遇しました。
Method 'getAccount' not found on controller S1AccountController. Check spelling, method exists, and/or method is RemoteAction annotated.
S1AccountControllerクラスのgetAccountメソッドが見つかりません..というエラーです。
このクラスもこのメソッドもちゃんと用意されているし、別の処理で実際に動いているのに。。と思ったのですが、原因は引数にセットされている値でした。
今回エラーが出た時の処理は、レコードIDを条件に対象レコードを取得するという処理です。
このレコードIDはAngularJSの『$routeParams.id』をつかって取得しようとしました。
$routeParams.idは値が存在しない場合、NULLでもブランク値でもなく『undefined』がセットされます。この『undefined』をRemoteActionの引数として渡そうとしたことでエラーが発生していました。
次のように値の存在判定をして、『undefined』の場合はNULLをセットするようにしておけば、このエラーを回避できます。
RemoteActionの処理でnot found on controllerというエラーが発生した場合はこのあたりもチェックしてみると良さそうです。
ApexのStringメソッドにはleftPadというメソッドが用意されています。指定の文字列を特定の文字数になるまで文字を追加できるメソッドです。
Apexガイドには引数の指定に文字数を指定できると記載されていますが、文字数の他に実際に追加する文字列も指定することが可能となっています。
String strVal = 'i'; String result = strVal.leftPad(3, '0');
というような感じです。
テストデータを用意するときにも便利です。
List<Account> accounts = new List<Account>(); for (Integer i = 0; i < 100; i++) { accounts.add(new Account(Name = 'デモ_' + String.valueOf(i).leftPad(3, '0'))); } insert accounts;
右側に追加したい場合はrightPadも用意されています。
Lightning Design SystemとAngularJSをつかってSalesforce1モバイル用のサンプルアプリをつくりました。シングルページアプリケーションになっています。
S1アプリ開発でscrollerjsを試してみました。
scrollerjsについてはこちら。
Chatterグループのグループアクセス設定に「ブロードキャストのみ」が追加されていました。
これを利用するとグループ所有者またはグループマネージャのみ新しい投稿を作成できるように制限することができるみたいです。また、グループメンバーはコメントのみ投稿可能となります。
投稿できるユーザを制限することで社内ニュース専用グループのような使い方ができると思います。
グループマネージャはロールの変更リンクから設定できます。
所有者やマネージャ以外のユーザがグループにアクセスすると新規投稿ができないようになっています。コメントや「いいね」、共有などの操作は可能でした。
Chatterグループ設定に新しく追加された「ブロードキャストのみ」設定はこんな感じでした。
Chatterグループのバナー画像設定はLightning Experience専用の機能です。グループ画面の右上にあるカメラアイコンから設定できます。
アップロードできる画像の条件は次のようになっています。
バナー画像で表示できる高さは決まっていますが、次のようにトリミングできるようになっていました。
アップロード後は次のように表示されます。
別の写真に変更したい場合は、再度カメラアイコンをクリックして編集リンクを選択します。
先ほどと同じ手順でアップロードとトリミングをしてあげると変更完了です。
バナー写真はグループだけでなく、ユーザの画面にも設定することができるようになっています。
Chatterグループにグループレポートリンクが追加されていました。ここからグループメンバーの活動状況をレポートで参照できるみたいです。
こちらがデフォルト設定のレポートです。投稿数やいいねの数などを確認することができます。
カスタマイズできる内容はこんな感じでした。
確認できるのは投稿数など基本的な情報で個々のメンバーの利用状況はこのレポートには含まれていなそうです。ですがグループ設定画面から参照できるのは便利そうでした。
ちなみに管理者以外の参照権限のないユーザの場合はグループレポートリンクは非表示になっていました。
グループの増加に伴うコラボレーションの増大ということで個々のユーザが参加できるChatterグループが最大300まで増加しました。各 Salesforce 組織には最大 30,000 のグループを含めることができます。
アーカイブ済みグループは、個人および組織レベルのグループの制限にカウントされないとのことなので、不要になったグループはアーカイブしておいた方がいいみたいです。
権限のあるユーザは、カスタムオブジェクト設定でその種別のカスタムオブジェクトレコードがグループで許可されていない場合でも、グループパブリッシャーを使用してそのレコードをグループ内で作成できるようになりました。
これまでは、「Chatter グループ内で許可」のチェックボックスがオフになっていると、ユーザはその種別のレコードをグループ内で作成できませんでした。
レコード作成権限のあるユーザは、そのオブジェクト種別のレコードをグループパブリッシャーから作成できるようになりました。
ただし、グループレコードへの紐付は行えないようになっています。
ちなみにグループアクションの作成とレイアウトの追加はこんな感じ。
Chatterグループ内で許可にチェックを付けておけば、グループレコードとして紐つけることが可能です。
グループレコードとして紐付くとレコードへのアクセスもしやすくなるので基本的にはChatterグループ内で許可にチェックしておくと良さそうです。
ちなみにチェック無しの時に作成したレコードは、後からチェック有りに変更されても自動で紐付くということはありませんでした。
Spring16からNapili コミュニティでのグループのサポートされるようになりました。Salesforce Classicでのみ利用可能みたいです。
Napiliコミュニティというのはコミュニティ作成時に選択できるNapiliテンプレートのことです。
テンプレートはコミュニティ作成後でも変更可能です。
Napiliテンプレート利用時の設定画面です。
今回追加されたというのはここのグループページ設定のことだと思います。
...なんですがコミュニティビルダーの設定方法をよくわかっていないので実際に有効化するところまでは確認できませんでした。
とりあえずページ設定したり先にコミュニティにグループを追加したりの作業が必要そうです。今回はこういうことが可能になったということを覚えておけばいいかなと思います。
顧客グループがLightning Experienceでも利用できるようになったみたいです。
グループアクセスのコレのことです。
Lightning Experienceの場合はこんな感じ。
追加手順は以下のとおりです。
グループに顧客を許可するには、まず非公開グループを作成してから、グループ設定で [顧客を許可] を選択します。[メンバーの追加] アクションを使用して、カスタマーユーザをメンバーとして追加します。[メンバーの追加] アクションが表示されない場合は、グループパブリッシャーレイアウトにこのアクションが含まれていることを確認します。