@manyos/smileconnect-api 1.41.4 → 1.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -91,6 +91,81 @@ function getFieldsForCreate(mapping, mappingNew, clientFields) {
91
91
  return targetList;
92
92
  }
93
93
 
94
+ function applyCustomFormMapping(object, mapping, requestIdField) {
95
+ //Apply mapping
96
+ Object.keys(mapping).forEach(remedyName => {
97
+ try {
98
+ const mappedName = mapping[remedyName]
99
+ Object.defineProperty(object, mappedName, Object.getOwnPropertyDescriptor(object, remedyName));
100
+ delete object[remedyName];
101
+ } catch (e) {
102
+ log.error('mappingError', e)
103
+ }
104
+ })
105
+ if (requestIdField) {
106
+ log.debug('Delete requestIdField', requestIdField);
107
+ delete object[requestIdField];
108
+ }
109
+ }
110
+
111
+ function applyCustomFormMapping2Remedy(entryData, mapping, constants, fields) {
112
+ //delete unmapped entries
113
+ const mappedFields = Object.values(mapping)
114
+
115
+ Object.keys(entryData).forEach(function (objectKey) {
116
+
117
+ const found = mappedFields.find(element => {
118
+ return element === objectKey;
119
+ });
120
+
121
+ if (found == null || found=== undefined) {
122
+ log.debug('delete', objectKey)
123
+ delete entryData[objectKey];
124
+ }
125
+ });
126
+
127
+ //Apply mapping
128
+ Object.keys(mapping).forEach(remedyName => {
129
+ try {
130
+ const mappedName = mapping[remedyName]
131
+ Object.defineProperty(entryData, remedyName, Object.getOwnPropertyDescriptor(entryData, mappedName));
132
+ delete entryData[mappedName];
133
+ } catch (e) {
134
+ log.error('mappingError', e)
135
+ }
136
+ })
137
+
138
+ //remove fields if no access
139
+ log.debug('Check for fields in', fields);
140
+ if (fields && Array.isArray(fields)) {
141
+ Object.keys(entryData).forEach(objectKey => {
142
+ const found = fields.find(element => {
143
+ return element === objectKey;
144
+ });
145
+ log.debug(`Found ${objectKey} in fields:`, found)
146
+
147
+ if (found == null || found=== undefined) {
148
+ log.debug('delete not accessible field', objectKey)
149
+ delete entryData[objectKey];
150
+ }
151
+ });
152
+ }
153
+
154
+ //add constant values (defaults)
155
+ if (constants && Array.isArray(constants) && constants.length) {
156
+
157
+ constants.forEach(function (element) {
158
+ log.debug('add constant', element);
159
+ try {
160
+ entryData[element.name] = element.value;
161
+ } catch (e) {
162
+ log.error(e);
163
+ }
164
+ });
165
+ }
166
+ return entryData;
167
+ }
168
+
94
169
  module.exports = {
95
- applyMapping2Remedy, getFieldsForCreate
170
+ applyMapping2Remedy, getFieldsForCreate, applyCustomFormMapping, applyCustomFormMapping2Remedy
96
171
  }
@@ -38,6 +38,7 @@ function applyMapping(object, mapping, requestIdField) {
38
38
  delete object[requestIdField];
39
39
  }
40
40
  }
41
+
41
42
  module.exports = {
42
43
  getIncludeArray, getLinkCI, applyMapping
43
44
  };
@@ -41,6 +41,33 @@ const fieldMappingItemSchema = {
41
41
  }*/
42
42
  };
43
43
 
44
+ const customFormMappingItemSchema = {
45
+ 'data' : {
46
+ in: ['body'],
47
+ exists: {
48
+ errorMessage: 'Is required'
49
+ }
50
+ },
51
+ 'data.formName': {
52
+ in: ['body'],
53
+ exists: {
54
+ errorMessage: 'Is required'
55
+ },
56
+ isString : {
57
+ errorMessage: 'Needs to be a string'
58
+ }
59
+ },
60
+ 'data.mapping': {
61
+ in: ['body'],
62
+ exists: {
63
+ errorMessage: 'Is required'
64
+ },
65
+ isObject : {
66
+ errorMessage: 'Needs to be an Object'
67
+ }
68
+ }
69
+ };
70
+
44
71
  const CMDBCreateDataSchema = {
45
72
  'classId' : {
46
73
  in: ['body'],
@@ -222,5 +249,5 @@ const fieldMappingSchema = {
222
249
  };
223
250
 
224
251
  module.exports = {
225
- fieldMappingSchema, fieldMappingItemSchema, validateCMDBDataSchema, CMDBCreateDataSchema
252
+ fieldMappingSchema, fieldMappingItemSchema, validateCMDBDataSchema, CMDBCreateDataSchema, customFormMappingItemSchema
226
253
  };
@@ -11,6 +11,17 @@ function applyMapping(searchString, mapping) {
11
11
  return mappedString;
12
12
  }
13
13
 
14
+ function applyCustomFormMapping(searchString, mapping) {
15
+ log.debug('Apply mapping on', searchString, mapping);
16
+ let mappedString = searchString;
17
+ const remedyFields = Object.keys(mapping)
18
+ remedyFields.forEach(remedyField => {
19
+ mappedString = mappedString.replace(new RegExp("'" + mapping[remedyField] + "'", 'g'), "'" + remedyField + "'");
20
+ });
21
+ log.debug('Mapped searchString', mappedString);
22
+ return mappedString;
23
+ }
24
+
14
25
  function applySortMapping(sortOptions, mapping) {
15
26
  log.debug('Apply mapping on', sortOptions, mapping);
16
27
  if (sortOptions == null || sortOptions == undefined) {
@@ -28,6 +39,24 @@ function applySortMapping(sortOptions, mapping) {
28
39
  return mappedOptions;
29
40
  }
30
41
 
42
+ function applyCustomFormSortMapping(sortOptions, mapping) {
43
+ log.debug('Apply mapping on', sortOptions, mapping);
44
+ if (sortOptions == null || sortOptions == undefined) {
45
+ return sortOptions;
46
+ }
47
+ const mappedOptions = {};
48
+ Object.keys(sortOptions).forEach(key => {
49
+ const remedyFields = Object.keys(mapping)
50
+ remedyFields.forEach(remedyField => {
51
+ if (mapping[remedyField] === key) {
52
+ mappedOptions[remedyField] = sortOptions[key];
53
+ }
54
+ });
55
+ });
56
+ log.debug('Mapped sortOptions', mappedOptions);
57
+ return mappedOptions;
58
+ }
59
+
31
60
  function getCustomFields(clientFields, mapping, requestedFields) {
32
61
  log.debug('get custom fields', requestedFields);
33
62
  log.debug('clientFields fields', clientFields);
@@ -48,6 +77,26 @@ function getCustomFields(clientFields, mapping, requestedFields) {
48
77
  }
49
78
  }
50
79
 
80
+ function getCustomFormFields(clientFields, mapping, requestedFields) {
81
+ log.debug('get custom fields', requestedFields);
82
+ log.debug('clientFields fields', clientFields);
83
+ //return clientFields if null
84
+ if (requestedFields === null || requestedFields === undefined || !Array.isArray(requestedFields)) {
85
+ log.debug('no custom fields requested. return clientFields');
86
+ return clientFields;
87
+ } else {
88
+ const customFields = [];
89
+ const mappedFields = mapCustomFormFieldNames(requestedFields, mapping);
90
+ mappedFields.forEach(requestedField => {
91
+ if (clientFields.includes(requestedField)) {
92
+ customFields.push(requestedField);
93
+ }
94
+ });
95
+ log.debug('customFields', customFields);
96
+ return customFields;
97
+ }
98
+ }
99
+
51
100
  function mapFieldNames(customFields, mapping) {
52
101
  log.debug('Apply field mapping on', customFields, mapping);
53
102
  const mappedFields = [];
@@ -62,6 +111,21 @@ function mapFieldNames(customFields, mapping) {
62
111
  return mappedFields;
63
112
  }
64
113
 
114
+ function mapCustomFormFieldNames(customFields, mapping) {
115
+ log.debug('Apply field mapping on', customFields, mapping);
116
+ const mappedFields = [];
117
+ customFields.forEach(customField => {
118
+ const remedyFields = Object.keys(mapping)
119
+ remedyFields.forEach(remedyField => {
120
+ if (mapping[remedyField] === customField) {
121
+ mappedFields.push(remedyField);
122
+ }
123
+ });
124
+ });
125
+ log.debug('mapped fields', mappedFields);
126
+ return mappedFields;
127
+ }
128
+
65
129
  function mapFieldName(customField, mapping) {
66
130
  const result = mapFieldNames([customField], mapping);
67
131
  if (result !== null && result !== undefined) {
@@ -144,5 +208,8 @@ module.exports = {
144
208
  getCustomFields,
145
209
  mapFieldName,
146
210
  mapFieldNameReverse,
147
- applyMapping2record
211
+ applyMapping2record,
212
+ applyCustomFormMapping,
213
+ applyCustomFormSortMapping,
214
+ getCustomFormFields
148
215
  };
@@ -1,19 +0,0 @@
1
- ---
2
- layout: page
3
- title: Getting started
4
- subtitle: Every beginning is hard. We want to help you get started as fast as possible. Follow these guides to learn the basics-
5
- ---
6
-
7
- First things first:
8
- * [How to get a token](howto/token.md)
9
- * [Setup your client and mapping config](howto/sample-config.md)
10
-
11
- Now start with the basic stuff:
12
- * [How to read an incident](howto/incidents.md#read-an-incident)
13
- * [How to create an incident](howto/incidents.md#create-an-incident)
14
- * [How to update an incident](howto/incidents.md#update-an-incident)
15
-
16
- Read on for more advanced usage:
17
- * [How to handle worklogs](./howto/incident-worklogs.md)
18
- * [How to deal with attachments](./howto/worklog-attachment.md)
19
-
package/docs/old/index.md DELETED
@@ -1,41 +0,0 @@
1
- ---
2
- layout: page
3
- title: SMILEconnect documention
4
- subtitle: Learn all about manyos ITSM API on these pages
5
- use-site-title: true
6
- bigimg: /img/gb-isapi.jpg
7
- ---
8
-
9
- Our ITSM API creates a RESTful layer on-top of BMCs ITSM Suite. It allows you to expose all of your services with an easy and powerful REST-API. You can perform create, read and update operations on Incidents, Changes, Problems, Work Orders, Tasks and Assets. With no need for customization.
10
-
11
- [Get our API Framework here](https://manyos.it/isapi)
12
-
13
- # General
14
-
15
- If you're new to our API visit our [Getting Started Guide](getting-started.md)
16
-
17
- # Config Options
18
-
19
- All config options can be set as Environment Variables.
20
-
21
- [See all config options here](general/config.md)
22
-
23
- # Architecture
24
-
25
- Learn how the different parts work together in out [Architecture Overview](general/architecture.md)
26
-
27
- # Access to Objects and Fields
28
-
29
- Access to Objects (e.g. Incidents, Changes, ...) and Fields (e.g. Summary, ...) is granted per User.
30
-
31
- A global mapping is applied to all fields before they are returned.
32
-
33
- [Learn more about clients and fields](general/field-management.md)
34
-
35
- # Events
36
-
37
- [See all events here](eventlog/events.md)
38
-
39
- # Workflow
40
-
41
- [Learn more about the Remedy Workflow](workflow.md)
@@ -1,123 +0,0 @@
1
-
2
- # Install SMILEconnect
3
-
4
- The complete the installation of SMILEconnect you need to complete the following 4 steps:
5
-
6
- 1. Import Deployment Package
7
- 2. Create Remedy User for SMILEconnect
8
- 3. Setup Docker Container
9
- 4. Import AI Jobs
10
-
11
- The following sections will guide you through those steps.
12
-
13
- ## Import Deployment Package
14
-
15
- Import the deployment zip file to the RDA (Remedy Deployment) Console and deploy the file, using your Midtier
16
- It will import the Workflow Objects and Remedy Group needed.
17
-
18
- Steps:
19
-
20
- 1. Login to Midtier as an Administrator
21
- 2. Open Remedy Deployment Console (Applications -> AR System Administration -> AR System Deployment Management Console)
22
- 3. Import the zip file (Transfer Package -> Import)
23
- 4. Deploy the Package (Operations -> Deploy)
24
-
25
-
26
-
27
- ## Create User
28
-
29
- - Open "CTM:People" Form and create a new user with the name "SMILEconnect" with administrative permissions, fixed license for all needed modules, and "Master"-permissions for each.
30
- - Open "User" Form, search for the user "SMILEconnect" and add the group "MYSTicketBroker" to the permissions.
31
-
32
- ## Setup Docker Container
33
- Create Folder smileconnect and create the file docker-compose.yml in the newly created folder.
34
- Create a new subfolder conf and place your client.json and mapping.json to this folder
35
-
36
-
37
-
38
- Example file:
39
-
40
- version: '3.4'
41
- services:
42
- api:
43
- image: manyos/smileconnect-api
44
- restart: always
45
- environment:
46
- - LOGLEVEL=DEBUG
47
- - BASEURL=http://rapi:8080
48
- - AR_SERVER=<AR Server>
49
- - AR_PORT=<AR PORT>
50
- - AR_USER=<YOUR SMILECONNECT USER>
51
- - AR_PASSWORD=<THE PASSWORD FOR THE SMILECONNECT USER>
52
- - CACHETLL_CMDB=166
53
- - CACHETTL_ORGDATA=719
54
- - CACHETTL_CHANGE=1
55
- - CACHETTL_INCIDENT=1
56
- - CACHETTL_TASK=5
57
- - EVENTLOG_ENABLE=true
58
- - FEDERATED_BASE_URI=<URL TO SMARTIT>
59
- - SSO_PUBLIC_KEY=<YOUR SSO PUBLIC KEY>
60
- - ADMIN_USERS=<COMMASEPERATED LIST OF ADMIN USERS (GUI Users)>
61
- volumes:
62
- - conf:/home/node/app/conf
63
-
64
-
65
- eventmanager:
66
- image: manyos/smileconnect-eventmanager
67
- restart: always
68
- environment:
69
- - LOGLEVEL=DEBUG
70
- - MQ_CONNECTION=amqp://rabbitmq:8sj238sk2nsu82@pier1
71
- - MQ_QUEUE=test
72
- - ISAPI_CLIENT_ID=<SSO USER FOR EVENTMANAGER>
73
- - ISAPI_CLIENT_SECRET=<SECRET FOR THE EVENTMANAGER>
74
- - ISAPI_URI=<URL TO YOUR SMILECONNECT REST API; Example: https://isapi.port.manyos.io >
75
- - ISAPI_SSO=<URL TO YOUR SSO REALM; Example: https://sso.manyos.it/auth/realms/itsmproxy >
76
- - BASEURL=http://rapi:8080
77
- - AR_SERVER=<AR Server>
78
- - AR_PORT=<AR PORT>
79
- - AR_USER=<YOUR SMILECONNECT USER>
80
- - AR_PASSWORD=<THE PASSWORD FOR THE SMILECONNECT USER>
81
- - SSO_PUBLIC_KEY=<YOUR SSO PUBLIC KEY>
82
- volumes:
83
- - conf:/home/node/app/conf
84
-
85
- rapi:
86
- image: manyos/rapi
87
- restart: always
88
- environment:
89
- - GRAILS_OPTS=-XX:MaxPermSize=1024m -Xmx4096M -server
90
-
91
-
92
- gui:
93
- image: manyos/smileconnect-gui
94
- restart: always
95
- environment:
96
- - REACT_APP_API_URL=<URL TO YOUR SMILECONNECT REST API; Example: https://isapi.port.manyos.io/ >
97
- - REACT_APP_SSO_URL=<URL TO YOUR SSO REALM; Example: https://sso.manyos.it/auth/realms/itsmproxy >
98
- - REACT_APP_GUI_URL=<URL TO YOUR GUI; Example: https://isapi-gui-1808.port.manyos.io/ >
99
- - REACT_APP_WIZZARD_URL=<URL TO YOUR MIDTIER; Example: https://midtier.port.manyos.io/arsys/forms/arserver/MYS:SMILEconnect_NewVendor >
100
- - REACT_APP_TOKEN_REFRESHTIME=30
101
- ports:
102
- - 3003:80
103
- volumes:
104
- - conf:/home/node/app/conf
105
-
106
-
107
- Save the file and start the stack using with docker-compose up -d
108
-
109
-
110
-
111
- ## Import AI Jobs
112
-
113
- Import the AI Job/Tranfsformation Templates for each module.
114
- After importing change the connection parameter to your AR Server.
115
-
116
- TIP: In kettle options (Tools->Opions) disable the option:
117
- Replace existing objects on open/import.
118
- Your connection will not be replaced on future imports
119
-
120
-
121
-
122
-
123
-
@@ -1,169 +0,0 @@
1
- # Post installation configuration
2
-
3
- ## Configure SmartIT
4
-
5
- ### Display Vendor Fields
6
- It is good practice to display the vendor fields
7
-
8
- Vendor
9
- Vendor Ticket Number
10
-
11
- for the modules you are using SMILEconnect.
12
-
13
- To display them follow those steps:
14
-
15
- 1. Login to SmartIT as an Administrator
16
- 2. Open Screen Configuration page
17
- 3. Open the module and add the fields
18
-
19
- For more information about this, check BMC documentation at https://docs.bmc.com
20
-
21
- ### Add Actions
22
-
23
- If you want to be able to send "de-registration" events to your partner systems, you need to create a new
24
- Provider Action for each module.
25
-
26
- 1. Login as Administrator to Midtier
27
- 2. Open Form "SMT:SmartIT_Provider Action Template Configuration"
28
- 3. Click new
29
- 4. Set name to SMILEconnect_<Module>_Unregister for example: SMILEconnect_INC_Unregister
30
- 5. Select your Source Form (HPD:Help Desk, CHG:Infrastructure Change, WOI:WorkOrder, TMS:Task)
31
- 6. Select the Data Source for your Source Form: (incident, change, workorder, task)
32
- 7. Set Status to Online
33
- 8. Click "New Mapping"
34
- 9. Set Mapping Type to "Input"
35
- 10. Select z1D Action as Source Field
36
- 11. Click Save
37
- 12. Click "New Mapping"
38
- 13. Set Mapping Type to "Input"
39
- 14. Select the Ticket Number (Incident Number, Infrastructure Change ID, Work Order ID) as Source Field
40
- 15. Click Save
41
-
42
- 16. Login to Smart IT as Administrator
43
- 17. Open Screen Configuration page
44
- 18. Click Add / Remove Actions for the module
45
- 19. Click Add Action
46
- 20. Set Template Name to the previous created name SMILEconnect_<Module>_Unregister
47
- 21. For z1D Action set "Default Value" "UnregisterSMILEconnect"
48
- 22. For Ticket Number set "From Ticket" "id"
49
-
50
-
51
- Optional:
52
-
53
- Associate with Vendor Fields
54
- Open Field Configuration select Associate Action, and select the action you created before.
55
-
56
-
57
- For more information about provider actions visit https://docs.bmc.com
58
-
59
-
60
-
61
-
62
-
63
- ##Configure SMILEconnect
64
-
65
- To Configure SMILEconnect we provide SMILEconnect GUI.
66
- This GUI will use the SMILEconnect REST API to configure clients and mappings.
67
-
68
- It will provide the following functions:
69
-
70
- - copy a client
71
- - remove a client
72
- - configure mapping (add mapping, edit mapping and remove mapping)
73
- - configure a client (add fields & constants, edit fields & constants, remove fields & constants)
74
- - export settings
75
- - import settings
76
-
77
-
78
- ### Configure mapping
79
-
80
- To Configure Field mapping you need to open "Field Mapping" from the Navigation Menu on the left hand side.
81
-
82
- On the top menu you can choose for which module you want to configure the mapping.
83
-
84
-
85
- #### Configure mapping for ITSM modules
86
-
87
- If you select the modules Incident, Change, Problem , WorkOrder, Task a sub top menu will open, and you can select the action for which you want to configure the mapping.
88
-
89
- For example: Incident, New Incident, Worklog
90
-
91
- 1. To add a new mapping click the "+" button in the right upper corner.
92
- 2. Speficy the Remedy Field Name as "Form Name" and the Mapping Name of your choice as "API Name"
93
- 3. Select ok to save the mapping.
94
-
95
- #### Configure mapping for CMDB classes
96
-
97
- If you need to specify a mapping for any class other than BaseElement you can use the "Add Class +" and add the Class of your choice.
98
- You just need to Enter the Name without prefix. For example "ComputerSystem"
99
- Then specify the fields the same way as above.
100
-
101
- To remove a class, just hide the trash icon next to the class name.
102
-
103
-
104
- ### Configure clients
105
-
106
- You find a list of clients the navigation menu on the left hand side.
107
- you can copy a client to a new one, delete a client directly in the menu by using the buttons next to the name.
108
-
109
- #### Configure permissions for ITSM modules
110
-
111
- On the top navigation bar you can select the menu you need to configure. In the sub navigation bar you see the available sections.
112
- You can add new fields or constants using the "+" button on the upper right corner of the table, or edit and remove fields and constants using the pencil or trash buttons.
113
-
114
-
115
- #### Configure permissions for CMDB classes
116
- If you need to specify a fields for any class other than BaseElement you can use the "Add Class +" and add the Class of your choice.
117
- You just need to Enter the Name without prefix. For example "ComputerSystem"
118
- Then specify the fields the same way as above.
119
-
120
- To remove a class, just hide the trash icon next to the class name.
121
-
122
- ### Configure client using ITSM Wizard
123
- If you need to send tickets to this client use the ITSM Wizard to configure this client in itsm and smartit.
124
-
125
- Select the ITSM Wizard in the left hand navigation to open the ITSM Wizard.
126
- Specify the
127
-
128
- Vendor
129
- Vendor Organization
130
- Vendor Group
131
- for the ITSM Suite and choose for which modules you want to enable this client.
132
-
133
- ### Export / Import Mapping and Clients
134
-
135
- Open Export / Import Section on the left and use the download button to save the config to your local drive.
136
- Use the upload button to upload a local file to smileconnect.
137
-
138
- ## Use Custom AI Job Templates
139
-
140
- We provide custom AI Job templates for outbound communication for
141
- incident
142
- change
143
- worklog
144
- problem
145
-
146
- those templates include error handling. For each module we provide two transformation.
147
- One for Ticket Modify Event, one for the Worklog Event
148
-
149
- This section will explain the usage of the template using change as example.
150
-
151
- 1. Save the template with a different name before you use it
152
- 2. open Get CHG_Modified Events and speficy the VendorID in the Qualification
153
- 3. open Set URLs and change Url to your SSO URL and UrlCHG to your Endpoint
154
- 4. Open Change Lookup step and provide all Fields in the return from lookup table section you need
155
- 5. Implement your mapping if needed. Use Map Status and Map Prio as samples.
156
- 6. If you are using OAUTH2 you can use the HTTP Post Step to get the token.
157
- 7. Open the "Create Ouput" to create the output you need (json sample is included)
158
- 8. "Rest Client" Step is a sample for the output, change it to the step you need. Rest, Soap, etc.
159
- 9. There is a dummy step, for the update, if the ticket has already been send.
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
@@ -1,19 +0,0 @@
1
- # Prepare your environment for the SMILEconnect installation
2
-
3
- # SSO environment
4
-
5
- 1. If you are already running an OIDC System create a new realm for SMILEconnect. if not you can setup for example Keykloak.
6
- 2. For each of your interfaces create a client with credential
7
- 3. Create a client with credentials for the SMILEconnect-Eventmanager
8
- 4. Create a client with implicit flow for SMILEconnectGUI and set the web-origin URL to your SMILEconnectGUI URL
9
- 5. Get the public certificate from your SSO Realm
10
-
11
-
12
- # Docker
13
- 1. Setup docker and docker-compose on your liunx server
14
- 2. Setup your server to access https://\*.docker.io/\* and https://\*.docker.com/\*
15
- 3. Login to the docker-environment with the docker-hub user we provided to you.
16
-
17
- docker login <username>
18
-
19
-
@@ -1,34 +0,0 @@
1
- ---
2
- layout: page
3
- title: 'Tag Index'
4
- ---
5
-
6
- {%- capture site_tags -%}
7
- {%- for tag in site.tags -%}
8
- {{- tag | first -}}{%- unless forloop.last -%},{%- endunless -%}
9
- {%- endfor -%}
10
- {%- endcapture -%}
11
- {%- assign tags_list = site_tags | split:',' | sort -%}
12
-
13
- {%- for tag in tags_list -%}
14
- <a href="#{{- tag -}}" class="btn btn-primary tag-btn"><i class="fa fa-tag" aria-hidden="true"></i>&nbsp;{{- tag -}}&nbsp;({{site.tags[tag].size}})</a>
15
- {%- endfor -%}
16
-
17
- <div id="full-tags-list">
18
- {%- for tag in tags_list -%}
19
- <h2 id="{{- tag -}}" class="linked-section">
20
- <i class="fa fa-tag" aria-hidden="true"></i>
21
- &nbsp;{{- tag -}}&nbsp;({{site.tags[tag].size}})
22
- </h2>
23
- <div class="post-list">
24
- {%- for post in site.tags[tag] -%}
25
- <div class="tag-entry">
26
- <a href="{{ post.url | relative_url }}">{{- post.title -}}</a>
27
- <div class="entry-date">
28
- <time datetime="{{- post.date | date_to_xmlschema -}}">{{- post.date | date: "%B %d, %Y" -}}</time>
29
- </div>
30
- </div>
31
- {%- endfor -%}
32
- </div>
33
- {%- endfor -%}
34
- </div>