Skip to content

Commit

Permalink
Merge pull request #73 from contentstack/next
Browse files Browse the repository at this point in the history
Fix: Empty string in case td or th node had void:true
  • Loading branch information
nadeem-cs authored Jun 5, 2024
2 parents a23057f + 20ef749 commit 4b8c4cd
Show file tree
Hide file tree
Showing 15 changed files with 185 additions and 69 deletions.
6 changes: 3 additions & 3 deletions AUTHORS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Authors

- [ishaileshmishra](shailesh.mishra@***REMOVED***)
- [shaileshmishra](mshaileshr@***REMOVED***)
- [admin](dev@***REMOVED***)
- [ishaileshmishra](shailesh.mishra@contentstack.com)
- [shaileshmishra](mshaileshr@contentstack.com)
- [admin](dev@contentstack.com)
6 changes: 6 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
A brief description of what changes project contains
## May 28, 2024

#### v1.2.11

- Fix: ignore td/th in case of attrs has void:true

## May 14, 2024

#### v1.2.10

-Enhancement: Update Asset url method added for GQL
Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ If you believe you have found a security vulnerability in any Contentstack-owned

**Please do not report security vulnerabilities through public GitHub issues.**

Send email to [security@***REMOVED***](mailto:security@***REMOVED***).
Send email to [security@contentstack.com](mailto:security@contentstack.com).

You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message.

Expand All @@ -24,4 +24,4 @@ Please include the requested information listed below (as much as you can provid

This information will help us triage your report more quickly.

[https://www.***REMOVED***/trust/](https://www.***REMOVED***/trust/)
[https://www.contentstack.com/trust/](https://www.contentstack.com/trust/)
10 changes: 5 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.contentstack.sdk</groupId>
<artifactId>utils</artifactId>
<version>1.2.10</version>
<version>1.2.11</version>
<packaging>jar</packaging>
<name>Contentstack-utils</name>
<description>Java Utils SDK for Contentstack Content Delivery API, Contentstack is a headless CMS</description>
<url>https://www.***REMOVED***</url>
<url>https://www.contentstack.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand All @@ -35,9 +35,9 @@
<developers>
<developer>
<name>shaileshmishra</name>
<email>shailesh.mishra@***REMOVED***</email>
<email>shailesh.mishra@contentstack.com</email>
<organization>contentstack</organization>
<organizationUrl>https://www.***REMOVED***/</organizationUrl>
<organizationUrl>https://www.contentstack.com/</organizationUrl>
</developer>
</developers>

Expand Down Expand Up @@ -73,7 +73,7 @@

<organization>
<name>Contentstack.</name>
<url>http://***REMOVED***</url>
<url>http://contentstack.com</url>
</organization>

<dependencies>
Expand Down
18 changes: 14 additions & 4 deletions src/main/java/com/contentstack/utils/render/DefaultOption.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

public class DefaultOption implements Option {


/**
* The function `renderOptions` takes in a JSON object and metadata and returns a string based on the
* style type of the metadata.
Expand Down Expand Up @@ -153,10 +154,19 @@ public String renderNode(String nodeType, JSONObject nodeObject, NodeCallback ca
return "<tfoot" + strAttrs + ">" + cleanChildren + "</tfoot>";
case "tr":
return "<tr" + strAttrs + ">" + cleanChildren + "</tr>";
case "th":
return "<th" + strAttrs + ">" + cleanChildren + "</th>";
case "td":
return "<td" + strAttrs + ">" + cleanChildren + "</td>";
case "th":{
if (nodeObject.has("attrs") && nodeObject.optJSONObject("attrs").has("void") &&
nodeObject.optJSONObject("attrs").optBoolean("void")) {
return "";
}else{
return "<th" + strAttrs + ">" + cleanChildren + "</th>";}}
case "td":{
if (nodeObject.has("attrs") && nodeObject.optJSONObject("attrs").has("void") &&
nodeObject.optJSONObject("attrs").optBoolean("void")) {
return "";
}else{
return "<td" + strAttrs + ">" + cleanChildren + "</td>";}}

case "blockquote":
return "<blockquote" + strAttrs + ">" + cleanChildren + "</blockquote>";
case "code":
Expand Down
100 changes: 100 additions & 0 deletions src/test/java/com/contentstack/utils/DefaultOptionTests.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package com.contentstack.utils;

import com.contentstack.utils.helper.Metadata;
import com.contentstack.utils.interfaces.NodeCallback;
import com.contentstack.utils.render.DefaultOption;


import org.json.JSONObject;
import org.json.JSONArray;
import org.jsoup.nodes.Attributes;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

import static org.junit.Assert.assertEquals;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -82,5 +88,99 @@ public void testEmbeddedDefaultDisplayable() {
String result = defaultOptions.renderOptions(localJsonObj.optJSONObject("_embedded_items"), metadata);
Assert.assertEquals("<img src=\"\" alt=\"\" />", result);
}
@Test
public void testRenderNodeWithVoidTd() {
DefaultOption defaultOptions = new DefaultOption();
JSONObject nodeObject = new JSONObject();
JSONObject attrs = new JSONObject();
attrs.put("void", true);
nodeObject.put("attrs", attrs);
nodeObject.put("children", new JSONArray());

NodeCallback callback = children -> {
// Simple callback implementation for testing purposes
StringBuilder sb = new StringBuilder();
for (int i = 0; i < children.length(); i++) {
sb.append(children.getJSONObject(i).getString("type"));
}
return sb.toString();
};

String result = defaultOptions.renderNode("td", nodeObject, callback);
Assert.assertEquals("", result);
}
@Test
public void testRenderNodeWithVoidTh() {
DefaultOption defaultOptions = new DefaultOption();
JSONObject nodeObject = new JSONObject();
JSONObject attrs = new JSONObject();
attrs.put("void", true);
nodeObject.put("attrs", attrs);
nodeObject.put("children", new JSONArray());

NodeCallback callback = children -> {
// Simple callback implementation for testing purposes
StringBuilder sb = new StringBuilder();
for (int i = 0; i < children.length(); i++) {
sb.append(children.getJSONObject(i).getString("type"));
}
return sb.toString();
};

String result = defaultOptions.renderNode("th", nodeObject, callback);
Assert.assertEquals("", result);
}

@Test
public void testRenderNodeWithoutVoidTd() {
DefaultOption defaultOptions = new DefaultOption();
JSONObject nodeObject = new JSONObject();
JSONObject attrs = new JSONObject();
attrs.put("class", "example");
nodeObject.put("attrs", attrs);
JSONArray children = new JSONArray();
JSONObject child = new JSONObject();
child.put("type", "text");
child.put("content", "example content");
children.put(child);
nodeObject.put("children", children);

NodeCallback callback = childrenArray -> {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < childrenArray.length(); i++) {
sb.append(childrenArray.getJSONObject(i).getString("content"));
}
return sb.toString();
};

String result = defaultOptions.renderNode("td", nodeObject, callback);
Assert.assertEquals("<td class=\"example\">example content</td>", result);
}

@Test
public void testRenderNodeWithoutVoidTh() {
DefaultOption defaultOptions = new DefaultOption();
JSONObject nodeObject = new JSONObject();
JSONObject attrs = new JSONObject();
attrs.put("class", "example");
nodeObject.put("attrs", attrs);
JSONArray children = new JSONArray();
JSONObject child = new JSONObject();
child.put("type", "text");
child.put("content", "example content");
children.put(child);
nodeObject.put("children", children);

NodeCallback callback = childrenArray -> {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < childrenArray.length(); i++) {
sb.append(childrenArray.getJSONObject(i).getString("content"));
}
return sb.toString();
};

String result = defaultOptions.renderNode("th", nodeObject, callback);
Assert.assertEquals("<th class=\"example\">example content</th>", result);
}

}
2 changes: 1 addition & 1 deletion src/test/java/com/contentstack/utils/RTEResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class RTEResult {
public static String kH6Html = "<h6>Nunc porta diam vitae purus semper, ut consequat lorem vehicula.</h6>";
public static String kOrderListHtml = "<ol><li redactor-attributes=\"{}\" style=\"text-align: justify; \">Morbi in quam molestie, fermentum diam vitae, bibendum ipsum.</li><li redactor-attributes=\"{}\" style=\"text-align: justify; \">Pellentesque mattis lacus in quam aliquam congue</li><li redactor-attributes=\"{}\" style=\"text-align: justify; \">Integer feugiat leo dignissim, lobortis enim vitae, mollis lectus.</li><li redactor-attributes=\"{}\" style=\"text-align: justify; \">Sed in ante lacinia, molestie metus eu, fringilla sapien.</li></ol>";
public static String kIUnorderListHtml = "<ul><li>Sed quis metus sed mi hendrerit mollis vel et odio.</li><li>Integer vitae sem dignissim, elementum libero vel, fringilla massa.</li><li>Integer imperdiet arcu sit amet tortor faucibus aliquet.</li><li>Aenean scelerisque velit vitae dui vehicula, at congue massa sagittis.</li></ul>";
public static String kImgHtml = "<img redactor-attributes=\"{\"asset_uid\":\"47f1aa5ae422cd1\"}\" width=\"33.69418132611637\" height=\"auto\" src=\"images.***REMOVED***/v3/assets/UID_13/Donald.jog.png\" />";
public static String kImgHtml = "<img redactor-attributes=\"{\"asset_uid\":\"47f1aa5ae422cd1\"}\" width=\"33.69418132611637\" height=\"auto\" src=\"images.contentstack.com/v3/assets/UID_13/Donald.jog.png\" />";
public static String kTableHtml = "<table><thead><tr><th><p>Header 1</p></th><th><p>Header 2</p></th></tr></thead><tbody><tr><td><p>Body row 1 data 1</p></td><td><p>Body row 1 data 2</p></td></tr><tr><td><p>Body row 2 data 1</p></td><td><p>Body row 2 data 2</p></td></tr></tbody></table>";
public static String kBlockquoteHtml = "<blockquote>Praesent eu ex sed nibh venenatis pretium.</blockquote>";
public static String kCodeHtml = "<code>Code template.</code>";
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/com/contentstack/utils/RTEString.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ public class RTEString {
public static String kH6Json = "{ \"uid\": \"06e34a71190849d7fcd\", \"_version\": 13, \"attrs\": { }, \"children\": [ { \"type\": \"h6\", \"attrs\": {}, \"uid\": \"c2dfa672242cfb7e2e1\", \"children\": [ { \"text\": \"Nunc porta diam vitae purus semper, ut consequat lorem vehicula.\", } ] } ], \"type\": \"doc\" }";
public static String kOrderListJson = "{ \"uid\":\"06e35 48119084 9d7fc2acd\", \"_version\":13, \"attrs\":{ }, \"children\":[{\"uid\":\"2b5b4acbb3cfce02d3e\",\"type\":\"ol\",\"children\":[{\"type\":\"li\",\"attrs\":{\"style\":{\"text-align\":\"justify\"},\"redactor-attributes\":{ }},\"uid\":\"160bbd7430b98bd3d996\",\"children\":[{\"text\":\"Morbi in quam molestie, fermentum diam vitae, bibendum ipsum.\"}]},{ \"type\":\"li\",\"attrs\":{ \"style\":{ \"text-align\":\"justify\"},\"redactor-attributes\":{ } },\"uid\":\"a15f4d749bc2903d\",\"children\":[{ \"text\":\"Pellentesque mattis lacus in quam aliquam congue\"}]},{ \"type\":\"li\",\"attrs\":{ \"style\":{ \"text-align\":\"justify\"},\"redactor-attributes\":{ } },\"uid\":\"e54224bbcb6f9e8f1e43\",\"children\":[{ \"text\":\"Integer feugiat leo dignissim, lobortis enim vitae, mollis lectus.\"}]},{ \"type\":\"li\",\"attrs\":{ \"style\":{ \"text-align\":\"justify\"},\"redactor-attributes\":{ } },\"uid\":\"c0148bab9af758784\",\"children\":[{ \"text\":\"Sed in ante lacinia, molestie metus eu, fringilla sapien.\"}]}],\"id\":\"7f413d448a\",\"attrs\":{ }}],\"type\":\"doc\"}";
public static String kUnorderListJson = "{ \"uid\":\"0e5481190849a\", \"_version\":13, \"attrs\":{ }, \"children\":[{\"uid\":\"a3a2b443ebffc867b\",\"type\":\"ul\",\"children\":[{\"uid\":\"f67354d4eed64451922\",\"type\":\"li\",\"children\":[{\"text\":\"Sed quis metus sed mi hendrerit mollis vel et odio.\"}],\"attrs\":{ }},{ \"uid\":\"5 50cba5 bea92f23e36fd1\",\"type\":\"li\",\"children\":[{ \"text\":\"Integer vitae sem dignissim, elementum libero vel, fringilla massa.\"}],\"attrs\":{ } },{ \"uid\":\"0e5c9b4cd983e8fd543\",\"type\":\"li\",\"children\":[{ \"text\":\"Integer imperdiet arcu sit amet tortor faucibus aliquet.\"}],\"attrs\":{ } },{ \"uid\":\"6d9a43a3816bd83a9a\",\"type\":\"li\",\"children\":[{ \"text\":\"Aenean scelerisque velit vitae dui vehicula, at congue massa sagittis.\"}],\"attrs\":{ } }],\"id\":\"b083fa46ef899420ab19\",\"attrs\":{ }}],\"type\":\"doc\"}";
public static String kImgJson = "{ \"uid\":\"06e34a7a4849d7fc2acd\", \"_version\":13, \"attrs\":{ }, \"children\":[{\"uid\":\"f3be74be3b64646e626\",\"type\":\"img\",\"attrs\":{\"url\":\"images.***REMOVED***/v3/assets/UID_13/Donald.jog.png\",\"width\":33.69418132611637,\"height\":\"auto\",\"redactor-attributes\":{\"asset_uid\":\"47f1aa5ae422cd1\"}},\"children\":[{\"text\":\"\"}]}],\"type\":\"doc\"}";
public static String kImgJson = "{ \"uid\":\"06e34a7a4849d7fc2acd\", \"_version\":13, \"attrs\":{ }, \"children\":[{\"uid\":\"f3be74be3b64646e626\",\"type\":\"img\",\"attrs\":{\"url\":\"images.contentstack.com/v3/assets/UID_13/Donald.jog.png\",\"width\":33.69418132611637,\"height\":\"auto\",\"redactor-attributes\":{\"asset_uid\":\"47f1aa5ae422cd1\"}},\"children\":[{\"text\":\"\"}]}],\"type\":\"doc\"}";
public static String kParagraphJson = "{ \"uid\":\"0d7fd\", \"_version\":13, \"attrs\":{ }, \"children\":[{\"type\":\"p\",\"attrs\":{},\"uid\":\"0a1b5676aa510e5a\",\"children\":[{\"text\":\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed condimentum iaculis magna in vehicula. Vestibulum vitae convallis lacus. Praesent a diam iaculis turpis rhoncus faucibus. Aliquam sed pulvinar sem.\"}]}],\"type\":\"doc\"}";
public static String kBlockquoteJson = "{ \"uid\":\"06084d7fd\", \"_version\":13, \"attrs\":{ }, \"children\":[{\"uid\":\"503f9cc97534db55\",\"type\":\"blockquote\",\"id\":\"431f78e567161460\",\"children\":[{\"text\":\"Praesent eu ex sed nibh venenatis pretium.\"}],\"attrs\":{ }}],\"type\":\"doc\"}";
public static String kCodeJson = "{ \"uid\":\"06ea490849d7fc2acd\", \"_version\":13, \"attrs\":{ }, \"children\":[{\"uid\":\"83fba92c91b30002b\",\"type\":\"code\",\"attrs\":{},\"children\":[{\"text\":\"Code template.\"}]}],\"type\":\"doc\"}";
public static String kTableJson = "{ \"uid\": \"06e481190849d7fcd\", \"_version\": 13, \"attrs\": { }, \"children\": [ { \"uid\": \"6dd64343bf634bfadd4\", \"type\": \"table\", \"attrs\": { \"rows\": 4, \"cols\": 2, \"colWidths\": [ 250, 250 ] }, \"children\": [ { \"uid\": \"b9082\", \"type\": \"thead\", \"attrs\": {}, \"children\": [ { \"type\": \"tr\", \"attrs\": {}, \"children\": [ { \"type\": \"th\", \"attrs\": {}, \"children\": [ { \"type\": \"p\", \"attrs\": {}, \"children\": [ { \"text\": \"Header 1\" } ], \"uid\": \"daa3ef\" } ], \"uid\": \"4b3124414a3\" }, { \"type\": \"th\", \"attrs\": { }, \"children\": [ { \"type\": \"p\", \"attrs\": { }, \"children\": [ { \"text\": \"Header 2\" } ], \"uid\": \"eae83c5797d\" } ], \"uid\": \"bca9b6f037a04fb485\" } ], \"uid\": \"b91ae7a48ef2e9da1\" } ] }, { \"type\": \"tbody\", \"attrs\": { }, \"children\": [ { \"type\": \"tr\", \"attrs\": { }, \"children\": [ { \"type\": \"td\", \"attrs\": { }, \"children\": [ { \"type\": \"p\", \"attrs\": { }, \"children\": [ { \"text\": \"Body row 1 data 1\" } ], \"uid\": \"ec674ccc5ce70b7cab\" } ], \"uid\": \"2a70bdeeb99a\" }, { \"type\": \"td\", \"attrs\": { }, \"children\": [ { \"type\": \"p\", \"attrs\": { }, \"children\": [ { \"text\": \"Body row 1 data 2\" } ], \"uid\": \"769a 3f9db34 ce8ec 10486d50\" } ], \"uid\": \"d6407 34a5c6 1ab1e5f7d1\" } ], \"uid\": \"77f6 b951c68 7f9eb397c5\" }, { \"type\": \"tr\", \"attrs\": { }, \"children\": [ { \"type\": \"td\", \"attrs\": { }, \"children\": [ { \"type\": \"p\", \"attrs\": { }, \"children\": [ { \"text\": \"Body row 2 data 1\" } ], \"uid\": \"a6bf 11bb48 630e87d721c0\" } ], \"uid\": \"3da39838b0feaf\" }, { \"type\": \"td\", \"attrs\": { }, \"children\": [ { \"type\": \"p\", \"attrs\": { }, \"children\": [ { \"text\": \"Body row 2 data 2\" } ], \"uid\": \"3b7d12 1f694202 49029e86313\" } ], \"uid\": \"95b38b04abcbc25e94f\" } ], \"uid\": \"b 227fea 8d247013 4f1e1e8\" } ], \"uid\": \"fd5ab86aa642798451b\" } ] }, ], \"type\": \"doc\" }";
public static String kLinkInPJson = "{ \"uid\":\"06e34a7190849d7f2acd\", \"_version\":13, \"attrs\":{ }, \"children\":[{\"type\":\"p\",\"attrs\":{\"style\":{\"text-align\":\"left\"},\"redactor-attributes\":{ }},\"uid\":\"d88dcdf4590dff2d\",\"children\":[{\"text\":\"\",\"bold\":true,\"italic\":true,\"underline\":true,\"subscript\":true},{ \"uid\":\"0d06598201aa8b47\",\"type\":\"a\",\"attrs\":{ \"url\":\"LINK.com\",\"target\":\"_self\"},\"children\":[{ \"text\":\"LINK\"}]},{ \"text\":\"\"}]}],\"type\":\"doc\"}";
public static String kEmbedJson = "{ \"uid\":\"06e34a7190849d7f2acd\", \"_version\":13, \"attrs\":{ }, \"children\":[{\"uid\":\"251017315905c35d42c9\",\"type\":\"embed\",\"attrs\":{\"url\":\"https://www.youtube.com/watch?v=AOP0yARiW8U\"},\"children\":[{\"text\":\"\"}]}],\"type\":\"doc\"}";
public static String kAssetReferenceJson = "{\"uid\":\"06e34a7 5e4 e549d \", \"_version\":1, \"attrs\":{}, \"children\":[{ \"uid\": \"4f7e33 3390a955 de10c1 c836\", \"type\":\"reference\",\"attrs\":{\"display-type\":\"display\",\"asset-uid\":\"UID_12\",\"content-type-uid\":\"sys_assets\",\"asset-link\":\"https://images.***REMOVED***/v3/assets/UID_14/11.jpg\",\"asset-name\":\"11.jpg\",\"asset-type\":\"image/jpeg\",\"type\":\"asset\",\"class-name\":\"embedded-asset\",\"width\":25.16914749661705,\"className\":\"dsd\",\"id\":\"sdf\"},\"children\":[{\"text\":\"\"}]}],\"type\":\"doc\"}";
public static String kAssetReferenceJson = "{\"uid\":\"06e34a7 5e4 e549d \", \"_version\":1, \"attrs\":{}, \"children\":[{ \"uid\": \"4f7e33 3390a955 de10c1 c836\", \"type\":\"reference\",\"attrs\":{\"display-type\":\"display\",\"asset-uid\":\"UID_12\",\"content-type-uid\":\"sys_assets\",\"asset-link\":\"https://images.contentstack.com/v3/assets/UID_14/11.jpg\",\"asset-name\":\"11.jpg\",\"asset-type\":\"image/jpeg\",\"type\":\"asset\",\"class-name\":\"embedded-asset\",\"width\":25.16914749661705,\"className\":\"dsd\",\"id\":\"sdf\"},\"children\":[{\"text\":\"\"}]}],\"type\":\"doc\"}";
public static String kEntryReferenceBlockJson = "{ \"uid\":\"06e34a7 5e4 e549d \", \"_version\":1, \"attrs\":{ }, \"children\":[{\"uid\":\"70f9b 325075d43 128c0d0 aa3eb7f291f\",\"type\":\"reference\",\"attrs\":{\"display-type\":\"block\",\"entry-uid\":\"UID_07\",\"content-type-uid\":\"content_block\",\"locale\":\"en-us\",\"type\":\"entry\",\"class-name\":\"embedded-entry\"},\"children\":[{\"text\":\"\"}]}],\"type\":\"doc\"}";
public static String kEntryReferenceLinkJson = "{ \"uid\":\"06e34a7 5e4 e549d\", \"_version\":1, \"attrs\":{ }, \"children\":[{\"uid\":\"7626ea98e0e95d602210\",\"type\":\"reference\",\"attrs\":{\"target\":\"_self\",\"href\":\"/copy-of-entry-final-02\",\"display-type\":\"link\",\"entry-uid\":\"UID_08\",\"content-type-uid\":\"embeddedrte\",\"locale\":\"en-us\",\"type\":\"entry\",\"class-name\":\"embedded-entry\"},\"children\":[{\"text\":\"/copy-of-entry-final-02\"}]}],\"type\":\"doc\"}";
public static String kEntryReferenceInlineJson = "{ \"uid\":\"06e34a7 5e4 e549d\", \"_version\":1, \"attrs\":{ }, \"children\":[{\"uid\":\"506 4878f3f46 s21f0cbc aff\",\"type\":\"reference\",\"attrs\":{\"display-type\":\"inline\",\"entry-uid\":\"UID_09\",\"content-type-uid\":\"embeddedrte\",\"locale\":\"en-us\",\"type\":\"entry\",\"class-name\":\"embedded-entry\"},\"children\":[{\"text\":\"\"}]}],\"type\":\"doc\"}";
Expand Down
Loading

0 comments on commit 4b8c4cd

Please sign in to comment.