You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

250 lines
15 KiB

<%@ page import="com.google.appengine.repackaged.com.google.gson.Gson" %>
<%@ page import="com.google.common.collect.ImmutableList" %>
<%@ page import="com.google.phonenumbers.ServletMain" %>
<%@ page import="com.google.phonenumbers.migrator.MigrationEntry" %>
<%@ page import="com.google.phonenumbers.migrator.MigrationResult" %>
<!DOCTYPE html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
final String E164_NUMBERS_LINK = "https://support.twilio.com/hc/en-us/articles/223183008-Formatting-International-Phone-Numbers";
final String COUNTRY_CODE_LINK = "https://countrycode.org/";
// TODO: use documentation link from base repository when forked repository has been merged in
final String DOCUMENTATION_LINK = "https://github.com/TomiwaOke/libphonenumber/tree/master/migrator/README.md";
final String ISSUE_TRACKER_LINK = "https://issuetracker.google.com/issues/new?component=192347";
final String GUIDELINES_LINK = "https://github.com/google/libphonenumber/blob/master/CONTRIBUTING.md#filing-a-code-issue";
final Gson gson = new Gson();
ImmutableList<MigrationResult> validMigrations = (ImmutableList<MigrationResult>) request.getAttribute("validMigrations");
ImmutableList<MigrationResult> invalidMigrations = (ImmutableList<MigrationResult>) request.getAttribute("invalidMigrations");
ImmutableList<MigrationEntry> validUntouchedNums = (ImmutableList<MigrationEntry>) request.getAttribute("validUntouchedNumbers");
ImmutableList<MigrationEntry> invalidUntouchedNums = (ImmutableList<MigrationEntry>) request.getAttribute("invalidUntouchedNumbers");
%>
<html>
<head>
<link type="text/css" rel="stylesheet" href="/stylesheets/servlet-main.css" />
<title>Migrator</title>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
const VALID_MIGRATIONS = 'Valid Migrations';
const INVALID_MIGRATIONS = 'Invalid Migrations';
const UNTOUCHED_VALID = 'Already Valid Numbers';
const UNTOUCHED_INVALID = 'Invalid Non-migratable Numbers';
const CHART_DESCRIPTIONS = new Map();
CHART_DESCRIPTIONS[VALID_MIGRATIONS] = 'The following are numbers that were successfully migrated by the tool:';
CHART_DESCRIPTIONS[INVALID_MIGRATIONS] = 'The following are numbers that were migrated by the tool but were not able' +
' to be verified as valid numbers based on metadata for the given country code:';
CHART_DESCRIPTIONS[UNTOUCHED_VALID] = 'The following are numbers that were already in valid formats:';
CHART_DESCRIPTIONS[UNTOUCHED_INVALID] = 'The following numbers were not seen as valid and could not be migrated based' +
' on the given country code:';
function getNumbersForSegment(selection) {
if (selection === VALID_MIGRATIONS) {
return <%=gson.toJson(ServletMain.getMigrationResultOutputList(validMigrations))%>;
} else if (selection === INVALID_MIGRATIONS) {
return <%=gson.toJson(ServletMain.getMigrationResultOutputList(invalidMigrations))%>;
} else if (selection === UNTOUCHED_VALID) {
return <%=gson.toJson(ServletMain.getMigrationEntryOutputList(validUntouchedNums))%>;
}
return <%=gson.toJson(ServletMain.getMigrationEntryOutputList(invalidUntouchedNums))%>;
}
google.charts.load('current', {packages:['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
const chartData = google.visualization.arrayToDataTable([
['Task', 'Frequency'],
[VALID_MIGRATIONS, <%= validMigrations != null ? validMigrations.size() : 0%>],
[INVALID_MIGRATIONS, <%= invalidMigrations != null ? invalidMigrations.size() : 0%>],
[UNTOUCHED_VALID, <%= validUntouchedNums != null ? validUntouchedNums.size() : 0%>],
[UNTOUCHED_INVALID, <%= invalidUntouchedNums != null ? invalidUntouchedNums.size() : 0%>]
]);
const chartProperties = {
pieHole: 0.4,
chartArea: { width: '90%', height: '100%' },
colors: [
<%=validMigrations != null && !validMigrations.isEmpty()%> ? '#277301' : '',
<%=invalidMigrations != null && !invalidMigrations.isEmpty()%> ? '#ffbf36' : '',
<%=validUntouchedNums != null && !validUntouchedNums.isEmpty()%> ? '#90ee90' : '',
<%=invalidUntouchedNums != null && !invalidUntouchedNums.isEmpty()%> ? '#ff472b' : '']
};
const modalBackdrop = document.getElementById("modalBackdrop");
document.getElementById("modalButton").onclick = function() {
document.getElementById("numbersList").innerHTML = '';
modalBackdrop.style.display = 'none';
};
window.onclick = function(event) {
if (event.target === modalBackdrop) {
document.getElementById("numbersList").innerHTML = '';
modalBackdrop.style.display = 'none';
}
};
function onSegmentClick() {
const selection = chart.getSelection()[0];
if (selection) {
const selectionName = chartData.getValue(selection.row, 0);
const numbersList = document.getElementById("numbersList");
const segmentNumbers = getNumbersForSegment(selectionName);
segmentNumbers.forEach(number => {
const value = document.createElement('li');
value.appendChild(document.createTextNode(number));
numbersList.appendChild(value);
});
document.getElementById("modalTitle").innerHTML = selectionName;
document.getElementById("modalDescription").innerHTML = CHART_DESCRIPTIONS[selectionName];
modalBackdrop.style.display = 'block';
}
}
const chart = new google.visualization.PieChart(document.getElementById('migration-chart'));
google.visualization.events.addListener(chart, 'select', onSegmentClick);
chart.draw(chartData, chartProperties);
}
</script>
</head>
<body>
<div class="page-heading">
<h1>Phone Number Migrator</h1>
<p>
The migrator is a tool which takes in a given <a href="<%=E164_NUMBERS_LINK%>" target="_blank">E.164 phone number(s)</a>
input as well as the corresponding BCP-47 <a href="<%=COUNTRY_CODE_LINK%>" target="_blank">country code</a>. The tool
will then check the validity of the phone number based on the country code and if possible, will convert the number
into a valid, dialable format.
</p>
<p>The following are the two available migration types that can be performed:</p>
<ul>
<li>
<strong>Single Number Migration:</strong> input a single E.164 phone number with its corresponding BCP-47 country
code. If there is an available migration that can be performed on the number, it will be converted to the new
format based on the specified migration rules.<br><br>
</li>
<li>
<strong>File Migration:</strong> input a text file containing one E.164 number per line along with the BCP-47
country code that corresponds to the numbers in the text file. All numbers in the text file that match available
migrations will be migrated and there will be the option of downloading a new text file containing the updated numbers.
By default, invalid migrations and numbers that did not go through a process of migration will be written to file
in their original text file format.
<br><br>
</li>
</ul>
<p>
For more information on the capabilities of the migrator as well as instructions on how to install the command line
tool, please view the <a href="<%=DOCUMENTATION_LINK%>" target="_blank">documentation</a>.
</p>
</div>
<div class="migration-result">
<%
if (request.getAttribute("numberError") == null && request.getAttribute("number") != null) {
if (request.getAttribute("validMigration") != null) {
out.print("<h3 class='valid'>Valid +" + request.getAttribute("numberCountryCode") + " Phone Number Produced!</h3>");
out.print("<p>The stale number '" + request.getAttribute("number") + "' was successfully migrated into the" +
" phone number: +" + request.getAttribute("validMigration") + "</p>");
} else if (request.getAttribute("invalidMigration") != null) {
out.print("<h3 class='invalid-migration'>Invalid +" + request.getAttribute("numberCountryCode") + " Migration</h3>");
out.print("<p>The stale number '" + request.getAttribute("number") + "' was migrated into the phone number:" +
" +" + request.getAttribute("invalidMigration") + ". However this was not seen as valid using our internal" +
" metadata for country code +" + request.getAttribute("numberCountryCode") + ".</p>");
} else if (request.getAttribute("alreadyValidNumber") != null) {
out.print("<h3 class='valid'>Already Valid +" + request.getAttribute("numberCountryCode") + " Phone Number!</h3>");
out.print("<p>The entered phone number was already seen as being in a valid, dialable format based on our" +
" metadata for country code +" + request.getAttribute("numberCountryCode") + ". Here is the number in" +
" its clean E.164 format: +" + request.getAttribute("alreadyValidNumber") + "</p>");
} else {
out.print("<h3 class='invalid-number'>Non-migratable +" + request.getAttribute("numberCountryCode") + " Phone Number</h3>");
out.print("<p>The phone number '" + request.getAttribute("number") + "' was not seen as a valid number and" +
" no migration recipe could be found for country code +" + request.getAttribute("numberCountryCode") +
" to migrate it. This may be because you have entered a country code which does not correctly correspond" +
" to the given phone number or the specified number has never been valid.</p>");
}
out.print("<p style='color: red; font-size: 14px'>Think there's an issue? File one <a href='" + ISSUE_TRACKER_LINK +
"' target='_blank'>here</a> following the given <a href='" + GUIDELINES_LINK + "' target='_blank'>guidelines</a>.</p>");
} else if (request.getAttribute("fileError") == null && request.getAttribute("fileName") != null) {
out.print("<h3>'" + request.getAttribute("fileName") + "' Migration Report for Country Code: +" + request.getAttribute("fileCountryCode") + "</h3>");
out.print("<p>Below is a chart showing the ratio of numbers from the entered file that were able to be migrated" +
" using '+" + request.getAttribute("fileCountryCode") + "' migration recipes. To understand more," +
" select a given segment from the chart below.</p>");
out.print("<div class='chart-wrap'><div id='migration-chart' class='chart'></div></div>");
out.print("<form action='" + request.getContextPath() + "/migrate' method='get' style='margin-bottom: 1rem'>");
out.print("<input type='hidden' name='countryCode' value='" + request.getAttribute("fileCountryCode") + "'/>");
out.print("<input type='hidden' name='fileName' value='" + request.getAttribute("fileName") + "'/>");
out.print("<input type='hidden' name='fileContent' value='" + request.getAttribute("fileContent") + "'/>");
out.print("<input type='submit' value='Export Results' class='button'/>");
out.print("</form>");
}
%>
</div>
<div class="migration-forms">
<div class="migration-form">
<h3>Single Number Migration</h3>
<div class="error-message"><%=request.getAttribute("numberError") == null ? "" : request.getAttribute("numberError")%></div>
<form action="${pageContext.request.contextPath}/migrate" method="post" enctype="multipart/form-data">
<label for="number">Phone number:</label>
<p>Enter a phone number in E.164 format. Inputted numbers can include spaces, curved brackets and hyphens</p>
<input type="text" name="number" id="number" placeholder="+841205555555" required
value="<%=request.getAttribute("number") == null ? "" : request.getAttribute("number")%>"/>
<label for="numberCountryCode">Country Code:</label>
<p>Enter the BCP-47 country code in which the specified E.164 phone number belongs to</p>
<input type="number" name="numberCountryCode" id="numberCountryCode" placeholder="84" required
value="<%=request.getAttribute("numberCountryCode") == null ? "" : request.getAttribute("numberCountryCode")%>"/>
<label for="numberCustomRecipe">Custom Recipe:</label>
<p>
(Optional) Upload a csv file containing a custom recipes table to be used for migrations. To understand how to
create a custom recipe file, please view the <a href="<%=DOCUMENTATION_LINK%>" target="_blank">documentation</a>.
</p>
<input type="file" name="customRecipe" id="numberCustomRecipe" accept=".csv"/>
<input type="submit" value="Migrate Number" class="button"/>
</form>
</div>
<div class="migration-form">
<h3>File Migration</h3>
<div class="error-message"><%=request.getAttribute("fileError") == null ? "" : request.getAttribute("fileError")%></div>
<form action="${pageContext.request.contextPath}/migrate" method="post" enctype="multipart/form-data">
<label for="file">File:</label>
<p>Upload a file containing one E.164 phone number per line. Numbers can include spaces, curved brackets and hyphens</p>
<input type="file" name="file" id="file" accept="text/plain" required/>
<label for="fileCountryCode">Country Code:</label>
<p>Enter the BCP-47 country code in which the E.164 phone numbers from the specified file belong to</p>
<input type="number" name="fileCountryCode" id="fileCountryCode" placeholder="84" required/>
<label for="fileCustomRecipe">Custom Recipe:</label>
<p>
(Optional) Upload a csv file containing a custom recipes table to be used for migrations. To understand how to
create a custom recipe file, please view the <a href="<%=DOCUMENTATION_LINK%>" target="_blank">documentation</a>.
</p>
<input type="file" name="customRecipe" id="fileCustomRecipe" accept=".csv"/>
<input type="submit" value="Migrate File" class="button"/>
</form>
</div>
</div>
<div id="modalBackdrop" class="modal-backdrop">
<div class="modal-content">
<h3 id="modalTitle"></h3>
<p id="modalDescription" style="color: grey; font-size: 12px"></p>
<div class="body">
<ul id="numbersList" style="padding-left: 1.5rem"></ul>
<p style="color: red; font-size: 14px">
Think there's an issue? File one <a href="<%=ISSUE_TRACKER_LINK%>" target="_blank">here</a>
following the given <a href="<%=GUIDELINES_LINK%>" target="_blank">guidelines</a>.
</p>
</div>
<button id="modalButton" class="button">Close</button>
</div>
</div>
</body>
</html>