Skip to content

Commit 87f7a16

Browse files
authored
Add search to code launcher's home page (#91)
* added loading of the data from the manifest and mapping for code examples pages * added mapping for texts on the homepages and code example pages * Added getting the manifest in the Quick_ACG project * removed leftover comments * Added support of the common texts and notes * Added form inputs and redirects * fixes after testing * fixes after code review * moved str_replace to service * fixed the link to example 28 to example 30 * added a search bar feature * added a dynamic switch of the API types * moved must_authenticate value to a constant * renamed the constant * added a double check of scopes for examples that have API calls in constructor * removing extra redirect with quickstart is true * removed extra redirect if quickstart is false * changed the language for SkipForLanguages in js file * fixing empty homepage if the user has CFR account enabled
1 parent ffb5657 commit 87f7a16

29 files changed

+500
-590
lines changed

Quick_ACG/src/RouterService.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use Example\Services\ManifestService;
77
use Example\Services\IRouterService;
88

9-
109
class RouterService implements IRouterService
1110
{
1211
private const CONTROLLER = [
@@ -83,10 +82,8 @@ public function router(): void
8382

8483
default:
8584
error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED);
86-
$_SESSION['API_TEXT'] = ManifestService::loadManifestData(ManifestService::getLinkToManifestFile('eSignature'));
87-
$controller = '\Example\Controllers\Examples\\' . $this->getController($page);
88-
// var_dump($controller);
89-
// die;
85+
$_SESSION['API_TEXT'] = ManifestService::loadManifestData($GLOBALS['DS_CONFIG']['CodeExamplesManifest']);
86+
$controller = '\Example\\' . $this->getController($page);
9087
new $controller($page);
9188
break;
9289
}

ds_config_example.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,7 @@
3131
'github_example_url' => 'https://github.com/docusign/code-examples-php/tree/master/src/Controllers/Examples',
3232
'documentation' => false,
3333
// Manifest files
34-
"ESignatureManifest" => "https://raw.githubusercontent.com/docusign/code-examples-csharp/master/manifest/eSignatureManifest.json",
35-
"ClickManifest" => "https://raw.githubusercontent.com/docusign/code-examples-csharp/master/manifest/ClickManifest.json",
36-
"RoomsManifest" => "https://raw.githubusercontent.com/docusign/code-examples-csharp/master/manifest/RoomsManifest.json",
37-
"MonitorManifest" => "https://raw.githubusercontent.com/docusign/code-examples-csharp/master/manifest/MonitorManifest.json",
38-
"AdminManifest" => "https://raw.githubusercontent.com/docusign/code-examples-csharp/master/manifest/AdminManifest.json"
34+
"CodeExamplesManifest" => "https://raw.githubusercontent.com/docusign/code-examples-csharp/master/manifest/CodeExamplesManifest.json"
3935
];
4036

4137
$JWT_CONFIG = [

public/assets/css.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,20 @@ margin-left: 10px;
148148

149149
.chooseApiTypeStyle {
150150
padding-left: 0;
151+
}
152+
153+
.has-search .form-control {
154+
padding-left: 2.375rem;
155+
}
156+
157+
.has-search .form-control-feedback {
158+
position: absolute;
159+
z-index: 2;
160+
display: block;
161+
width: 2.375rem;
162+
height: 2.375rem;
163+
line-height: 2.375rem;
164+
text-align: center;
165+
pointer-events: none;
166+
color: #aaa;
151167
}

public/assets/search.js

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
let DS_SEARCH = (function () {
2+
const API_TYPES = {
3+
ESIGNATURE: 'esignature',
4+
MONITOR: 'monitor',
5+
CLICK: 'click',
6+
ROOMS: 'rooms',
7+
ADMIN: 'admin',
8+
};
9+
10+
let processJSONData = function() {
11+
let json_raw = $("#api_json_data").text()
12+
, json = json_raw ? JSON.parse(json_raw) : false;
13+
14+
return json;
15+
}
16+
17+
let processCFR11Value = function () {
18+
let json_raw = $("#cfr11_data").text();
19+
20+
return json_raw;
21+
}
22+
23+
function checkIfExampleMatches(example, matches) {
24+
const name = example.ExampleName;
25+
const description = example.ExampleDescription;
26+
const pathNames = example.LinksToAPIMethod.map(a => a.PathName);
27+
28+
for (let i = 0; i < matches.length; i++) {
29+
if (name === matches[i].value || description === matches[i].value || pathNames.indexOf(matches[i].value) > -1) {
30+
return true;
31+
}
32+
}
33+
34+
return false;
35+
}
36+
37+
function clearNonMatchingExamples(examples, matches) {
38+
for (let i = examples.length - 1; i >= 0; i--) {
39+
if (!checkIfExampleMatches(examples[i], matches)) {
40+
examples.splice(i, 1);
41+
}
42+
}
43+
}
44+
45+
function clearResultsAfterMatching(api, matches) {
46+
const groups = api.Groups;
47+
48+
for (let i = groups.length - 1; i >= 0; i--) {
49+
const group = groups[i];
50+
clearNonMatchingExamples(group.Examples, matches);
51+
52+
if (group.Examples.length === 0) {
53+
groups.splice(i, 1);
54+
}
55+
}
56+
}
57+
58+
let findCodeExamplesByKeywords = function(json, pattern) {
59+
const options = {
60+
isCaseSensitive: false,
61+
minMatchCharLength: pattern.length,
62+
threshold: -0.0,
63+
includeMatches: true,
64+
ignoreLocation: true,
65+
useExtendedSearch: true,
66+
keys: [
67+
"Groups.Examples.ExampleName",
68+
"Groups.Examples.ExampleDescription",
69+
"Groups.Examples.LinksToAPIMethod.PathName"
70+
]
71+
};
72+
73+
const fuse = new Fuse(json, options);
74+
75+
var searchResults = fuse.search(JSON.stringify(pattern))
76+
77+
searchResults.forEach(searchResult => clearResultsAfterMatching(searchResult.item, searchResult.matches));
78+
79+
return searchResults;
80+
}
81+
82+
let getExamplesByAPIType = function(apiType, codeExamples) {
83+
let codeExamplesByAPI = codeExamples.find(x => x.Name.toLowerCase() === apiType);
84+
85+
if (codeExamplesByAPI != null) {
86+
return [codeExamplesByAPI];
87+
} else {
88+
return null;
89+
}
90+
}
91+
92+
let getEnteredAPIType = function(inputValue) {
93+
const inputLength = inputValue.length;
94+
95+
for (const key in API_TYPES) {
96+
if (Object.hasOwnProperty.call(API_TYPES, key)) {
97+
const apiType = API_TYPES[key];
98+
const comparedValue = apiType.substr(0, inputLength);
99+
100+
if(inputValue === comparedValue){
101+
return apiType;
102+
}
103+
}
104+
}
105+
106+
return null;
107+
}
108+
109+
function getLinkForApiType(apiName) {
110+
switch (apiName) {
111+
case API_TYPES.ADMIN:
112+
return "aeg";
113+
case API_TYPES.CLICK:
114+
return "ceg";
115+
case API_TYPES.ROOMS:
116+
return "reg";
117+
case API_TYPES.MONITOR:
118+
return "meg";
119+
case API_TYPES.ESIGNATURE:
120+
return "eg"
121+
}
122+
}
123+
124+
let addCodeExampleToHomepage = function(codeExamples) {
125+
var cfrPart11 = processCFR11Value();
126+
127+
codeExamples.forEach(
128+
element => {
129+
let linkToCodeExample = getLinkForApiType(element.Name.toLowerCase());
130+
131+
element.Groups.forEach(
132+
group => {
133+
$("#filtered_code_examples").append("<h2>" + group.Name + "</h2>");
134+
135+
group.Examples.forEach(
136+
example => {
137+
if (!example.SkipForLanguages || !example.SkipForLanguages.toLowerCase().includes("php"))
138+
{
139+
if (element.Name.toLowerCase() !== API_TYPES.ESIGNATURE.toLowerCase() ||
140+
((example.CFREnabled == "AllAccounts") ||
141+
((cfrPart11 == "enabled") && (example.CFREnabled == "CFROnly")) ||
142+
((cfrPart11 != "enabled") && (example.CFREnabled == "NonCFR"))))
143+
{
144+
$("#filtered_code_examples").append(
145+
"<h4 id="
146+
+ "example".concat("0".repeat(3 - example.ExampleNumber.toString().length)).concat(example.ExampleNumber) + ">"
147+
+ "<a href = 'index.php?page="
148+
+ linkToCodeExample.concat("0".repeat(3 - example.ExampleNumber.toString().length)).concat(example.ExampleNumber)
149+
+ "' >"
150+
+ example.ExampleName
151+
+ "</a ></h4 >"
152+
);
153+
154+
$("#filtered_code_examples").append("<p>" + example.ExampleDescription + "</p>");
155+
156+
$("#filtered_code_examples").append("<p>");
157+
158+
if (example.LinksToAPIMethod.length == 1)
159+
{
160+
$("#filtered_code_examples").append(processJSONData().SupportingTexts.APIMethodUsed);
161+
}
162+
else
163+
{
164+
$("#filtered_code_examples").append(processJSONData().SupportingTexts.APIMethodUsedPlural);
165+
}
166+
167+
for (let index = 0; index < example.LinksToAPIMethod.length; index++) {
168+
$("#filtered_code_examples").append(
169+
" <a target='_blank' href='"
170+
+ example.LinksToAPIMethod[index].Path
171+
+ "'>"
172+
+ example.LinksToAPIMethod[index].PathName
173+
+ "</a>"
174+
);
175+
176+
if (index + 1 === example.LinksToAPIMethod.length)
177+
{
178+
$("#filtered_code_examples").append("<span></span>");
179+
}
180+
else if (index + 1 === example.LinksToAPIMethod.length - 1)
181+
{
182+
$("#filtered_code_examples").append("<span> and </span>");
183+
}
184+
else
185+
{
186+
$("#filtered_code_examples").append("<span>, </span>");
187+
}
188+
189+
}
190+
191+
$("#filtered_code_examples").append("</p> ");
192+
}
193+
}
194+
}
195+
);
196+
}
197+
);
198+
}
199+
);
200+
}
201+
202+
let textCouldNotBeFound = function() {
203+
$("#filtered_code_examples").append(processJSONData().SupportingTexts.SearchFailed);
204+
}
205+
206+
return {
207+
processJSONData: processJSONData,
208+
getEnteredAPIType: getEnteredAPIType,
209+
getExamplesByAPIType: getExamplesByAPIType,
210+
findCodeExamplesByKeywords: findCodeExamplesByKeywords,
211+
textCouldNotBeFound: textCouldNotBeFound,
212+
addCodeExampleToHomepage: addCodeExampleToHomepage
213+
}
214+
})();
215+
216+
const input = document.getElementById('code_example_search');
217+
const log = document.getElementById('values');
218+
219+
input.addEventListener('input', updateValue);
220+
221+
function updateValue(esearchPattern) {
222+
document.getElementById('filtered_code_examples').innerHTML = "";
223+
224+
const inputValue = esearchPattern.target.value.toLowerCase();
225+
const json = DS_SEARCH.processJSONData().APIs;
226+
227+
if (inputValue === "")
228+
{
229+
DS_SEARCH.addCodeExampleToHomepage(json);
230+
}
231+
else
232+
{
233+
const apiType = DS_SEARCH.getEnteredAPIType(inputValue);
234+
235+
if (apiType !== null)
236+
{
237+
const adminExamples = DS_SEARCH.getExamplesByAPIType(apiType, json);
238+
DS_SEARCH.addCodeExampleToHomepage(adminExamples);
239+
}
240+
else
241+
{
242+
const result = DS_SEARCH.findCodeExamplesByKeywords(json, inputValue);
243+
if (result.length < 1) {
244+
DS_SEARCH.textCouldNotBeFound();
245+
} else {
246+
result.forEach(x => {
247+
DS_SEARCH.addCodeExampleToHomepage([x.item]);
248+
});
249+
}
250+
}
251+
}
252+
}

src/Controllers/AdminApiBaseController.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use Example\Services\AdminApiClientService;
66
use Example\Services\RouterService;
7+
use Example\Services\ApiTypes;
8+
use Example\Services\ManifestService;
79

810
abstract class AdminApiBaseController extends BaseController
911
{
@@ -18,6 +20,9 @@ public function __construct()
1820
$this->args = $this->getTemplateArgs();
1921
$this->clientService = new AdminApiClientService($this->args);
2022
$this->routerService = new RouterService();
23+
if (defined("static::EG")) {
24+
$this->checkDsToken();
25+
}
2126
}
2227
/**
2328
* Base controller
@@ -75,7 +80,9 @@ private function getController(
7580
}
7681
else
7782
{
78-
if ($this->routerService->ds_token_ok()) {
83+
$currentAPI = ManifestService::getAPIByLink(static::EG);
84+
85+
if ($this->routerService->ds_token_ok() && $currentAPI === $_SESSION['api_type']) {
7986
$GLOBALS['twig']->display($this->routerService->getTemplate(static::EG), [
8087
'title' => $this->routerService->getTitle(static::EG),
8188
'source_file' => basename(static::FILE),
@@ -92,8 +99,9 @@ private function getController(
9299
}
93100
else {
94101
# Save the current operation so it will be resumed after authentication
102+
$_SESSION['prefered_api_type'] = ApiTypes::Admin;
95103
$_SESSION['eg'] = $GLOBALS['app_url'] . 'index.php?page=' . static::EG;
96-
header('Location: ' . $GLOBALS['app_url'] . 'index.php?page=select_api');
104+
header('Location: ' . $GLOBALS['app_url'] . 'index.php?page=' . static::LOGIN_REDIRECT);
97105
exit;
98106
}
99107
}
@@ -104,7 +112,10 @@ private function getController(
104112
*/
105113
protected function checkDsToken(): void
106114
{
107-
if (!$this->routerService->ds_token_ok(self::MINIMUM_BUFFER_MIN)) {
115+
$currentAPI = ManifestService::getAPIByLink(static::EG);
116+
117+
if (!$this->routerService->ds_token_ok(self::MINIMUM_BUFFER_MIN) || $currentAPI !== $_SESSION['api_type']) {
118+
$_SESSION['prefered_api_type'] = ApiTypes::Admin;
108119
$this->clientService->needToReAuth(static::EG);
109120
}
110121
}

0 commit comments

Comments
 (0)