Add schedule twin update and invoke method UI

Add opendb file to .gitignore
Remove miss compiled main.css file

Merged files from remote

Add opendb file to .ignore , revert temp local change

Remove webcomplier realted configs from project

Fix merge issue
This commit is contained in:
Ce Li (Chinasoft) 2016-11-23 14:05:01 +08:00
Родитель e1d07fa1b8
Коммит a5fb94c4a6
28 изменённых файлов: 2037 добавлений и 11 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -8,6 +8,7 @@
*.suo
*.user
*.sln.docstates
*.opendb
# Build results
[Dd]ebug/

Просмотреть файл

@ -529,6 +529,15 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to Clear.
/// </summary>
public static string Clear {
get {
return ResourceManager.GetString("Clear", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to - CLEAR CLAUSE.
/// </summary>
@ -2383,6 +2392,15 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to MAXEXECUTION TIME.
/// </summary>
public static string MaxExecutionTime {
get {
return ResourceManager.GetString("MaxExecutionTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Price cannot be longer than 1024 characters..
/// </summary>
@ -2419,6 +2437,15 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to Method.
/// </summary>
public static string Method {
get {
return ResourceManager.GetString("Method", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Microsoft Azure.
/// </summary>
@ -2896,6 +2923,15 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to Query.
/// </summary>
public static string Query {
get {
return ResourceManager.GetString("Query", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The query name or filters are empty..
/// </summary>
@ -3085,6 +3121,15 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to This field is required.
/// </summary>
public static string RequiredValidationString {
get {
return ResourceManager.GetString("RequiredValidationString", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This field is required.
/// </summary>
@ -3319,6 +3364,51 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to Change Desired Properties.
/// </summary>
public static string ScheduleChangeDesiredProperties {
get {
return ResourceManager.GetString("ScheduleChangeDesiredProperties", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Change Tags.
/// </summary>
public static string ScheduleChangeTags {
get {
return ResourceManager.GetString("ScheduleChangeTags", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Desired Property Name.
/// </summary>
public static string ScheduleDesiredPropertyName {
get {
return ResourceManager.GetString("ScheduleDesiredPropertyName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit Properties or Tags.
/// </summary>
public static string ScheduleEditPropertiesOrTags {
get {
return ResourceManager.GetString("ScheduleEditPropertiesOrTags", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit Value.
/// </summary>
public static string ScheduleEditValue {
get {
return ResourceManager.GetString("ScheduleEditValue", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Schedule Job.
/// </summary>
@ -3337,6 +3427,24 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to Job Name.
/// </summary>
public static string ScheduleJobName {
get {
return ResourceManager.GetString("ScheduleJobName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Enter you job name here.
/// </summary>
public static string ScheduleJobNamePlaceHolder {
get {
return ResourceManager.GetString("ScheduleJobNamePlaceHolder", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to SCHEDULE JOB.
/// </summary>
@ -3346,6 +3454,33 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to Parameters.
/// </summary>
public static string ScheduleParameterName {
get {
return ResourceManager.GetString("ScheduleParameterName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Parameters.
/// </summary>
public static string ScheduleParameters {
get {
return ResourceManager.GetString("ScheduleParameters", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Set Job Time.
/// </summary>
public static string ScheduleSetJobTime {
get {
return ResourceManager.GetString("ScheduleSetJobTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to SEARCH DEVICES.
/// </summary>
@ -3634,6 +3769,33 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to {0} of {1} devices inapplicable.
/// </summary>
public static string SomeDeviceInapplicable {
get {
return ResourceManager.GetString("SomeDeviceInapplicable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} of {1} devices applicable.
/// </summary>
public static string SomeDevicesApplicable {
get {
return ResourceManager.GetString("SomeDevicesApplicable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to START TIME.
/// </summary>
public static string StartTime {
get {
return ResourceManager.GetString("StartTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Status.
/// </summary>
@ -3697,6 +3859,15 @@ namespace GlobalResources {
}
}
/// <summary>
/// Looks up a localized string similar to Tag Name.
/// </summary>
public static string TagName {
get {
return ResourceManager.GetString("TagName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Tags.
/// </summary>

Просмотреть файл

@ -1734,6 +1734,10 @@
<value>View all ({0})</value>
<comment>Button label.</comment>
</data>
<data name="Clear" xml:space="preserve">
<value>Clear</value>
<comment>Hyperlink label</comment>
</data>
<data name="SelectedColumns" xml:space="preserve">
<value>Selected Columns</value>
<comment>Section heading.</comment>
@ -1750,6 +1754,66 @@
<value>Delete</value>
<comment>Button label.</comment>
</data>
<data name="MaxExecutionTime" xml:space="preserve">
<value>MAXEXECUTION TIME</value>
<comment>Input label</comment>
</data>
<data name="Method" xml:space="preserve">
<value>Method</value>
<comment>Input label</comment>
</data>
<data name="Query" xml:space="preserve">
<value>Query</value>
<comment>Hyperlink label</comment>
</data>
<data name="ScheduleChangeDesiredProperties" xml:space="preserve">
<value>Change Desired Properties</value>
<comment>Input label</comment>
</data>
<data name="ScheduleChangeTags" xml:space="preserve">
<value>Change Tags</value>
<comment>Input label</comment>
</data>
<data name="ScheduleDesiredPropertyName" xml:space="preserve">
<value>Desired Property Name</value>
<comment>Input label</comment>
</data>
<data name="ScheduleEditPropertiesOrTags" xml:space="preserve">
<value>Edit Properties or Tags</value>
<comment>Label</comment>
</data>
<data name="ScheduleEditValue" xml:space="preserve">
<value>Edit Value</value>
<comment>Input label</comment>
</data>
<data name="ScheduleJobName" xml:space="preserve">
<value>Job Name</value>
<comment>Input label</comment>
</data>
<data name="ScheduleSetJobTime" xml:space="preserve">
<value>Set Job Time</value>
<comment>Input label</comment>
</data>
<data name="SomeDeviceInapplicable" xml:space="preserve">
<value>{0} of {1} devices inapplicable</value>
<comment>label</comment>
</data>
<data name="SomeDevicesApplicable" xml:space="preserve">
<value>{0} of {1} devices applicable</value>
<comment>label</comment>
</data>
<data name="StartTime" xml:space="preserve">
<value>START TIME</value>
<comment>Input label</comment>
</data>
<data name="TagName" xml:space="preserve">
<value>Tag Name</value>
<comment>Input label</comment>
</data>
<data name="ScheduleJobNamePlaceHolder" xml:space="preserve">
<value>Enter you job name here</value>
<comment>Placeholder message</comment>
</data>
<data name="MoveDown" xml:space="preserve">
<value>Move down</value>
<comment>Button label.</comment>
@ -1901,4 +1965,15 @@
<value>Unable to schedule job. An unexpected error occurred.</value>
<comment>Error message.</comment>
</data>
<data name="RequiredValidationString" xml:space="preserve">
<value>This field is required</value>
<comment>Error message</comment>
</data>
<data name="ScheduleParameterName" xml:space="preserve">
<value>Parameters</value>
</data>
<data name="ScheduleParameters" xml:space="preserve">
<value>Parameters</value>
<comment>Input Label</comment>
</data>
</root>

Просмотреть файл

@ -31,6 +31,9 @@ namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web
bundles.Add(new ScriptBundle("~/bundles/bootstrap")
.Include(
"~/Scripts/bootstrap.min.js"));
bundles.Add(new ScriptBundle("~/bundles/bootstrapdatetime")
.Include(
"~/Scripts/bootstrap-datetimepicker.min.js"));
bundles.Add(new ScriptBundle("~/bundles/knockout").Include(
"~/Scripts/knockout-{version}.js"));

Просмотреть файл

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.App_Start
{
public class DateTimeBinder : IModelBinder
{
public String DateFormat { get; set; }
public String TimeFormat { get; set; }
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
DateTime result;
if (DateTime.TryParseExact(value.AttemptedValue, DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None,out result))
{
return result;
}
else if(DateTime.TryParseExact(value.AttemptedValue, TimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
{
return result;
}
else if(DateTime.TryParse(value.AttemptedValue, out result))
{
return result;
}
return null;
}
}
public class NullableDateTimeBinder : IModelBinder
{
public String DateFormat { get; set; }
public String TimeFormat { get; set; }
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
DateTime result;
if (value !=null && DateTime.TryParseExact(value.AttemptedValue, DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
{
return result;
}
else if (value != null && DateTime.TryParseExact(value.AttemptedValue, TimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
{
return result;
}
else if (DateTime.TryParse(value.AttemptedValue, out result))
{
return result;
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,352 @@
/*!
* Datetimepicker for Bootstrap 3
* version : 4.17.43
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/
@bs-datetimepicker-timepicker-font-size: 1.2em;
@bs-datetimepicker-active-bg: @btn-primary-bg;
@bs-datetimepicker-active-color: @btn-primary-color;
@bs-datetimepicker-border-radius: @border-radius-base;
@bs-datetimepicker-btn-hover-bg: @gray-lighter;
@bs-datetimepicker-disabled-color: @gray-light;
@bs-datetimepicker-alternate-color: @gray-light;
@bs-datetimepicker-secondary-border-color: #ccc;
@bs-datetimepicker-secondary-border-color-rgba: rgba(0, 0, 0, 0.2);
@bs-datetimepicker-primary-border-color: white;
@bs-datetimepicker-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
.bootstrap-datetimepicker-widget {
list-style: none;
&.dropdown-menu {
margin: 2px 0;
padding: 4px;
width: 19em;
&.timepicker-sbs {
@media (min-width: @screen-sm-min) {
width: 38em;
}
@media (min-width: @screen-md-min) {
width: 38em;
}
@media (min-width: @screen-lg-min) {
width: 38em;
}
}
&:before, &:after {
content: '';
display: inline-block;
position: absolute;
}
&.bottom {
&:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid @bs-datetimepicker-secondary-border-color;
border-bottom-color: @bs-datetimepicker-secondary-border-color-rgba;
top: -7px;
left: 7px;
}
&:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid @bs-datetimepicker-primary-border-color;
top: -6px;
left: 8px;
}
}
&.top {
&:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid @bs-datetimepicker-secondary-border-color;
border-top-color: @bs-datetimepicker-secondary-border-color-rgba;
bottom: -7px;
left: 6px;
}
&:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid @bs-datetimepicker-primary-border-color;
bottom: -6px;
left: 7px;
}
}
&.pull-right {
&:before {
left: auto;
right: 6px;
}
&:after {
left: auto;
right: 7px;
}
}
}
.list-unstyled {
margin: 0;
}
a[data-action] {
padding: 6px 0;
}
a[data-action]:active {
box-shadow: none;
}
.timepicker-hour, .timepicker-minute, .timepicker-second {
width: 54px;
font-weight: bold;
font-size: @bs-datetimepicker-timepicker-font-size;
margin: 0;
}
button[data-action] {
padding: 6px;
}
.btn[data-action="incrementHours"]::after {
.sr-only();
content: "Increment Hours";
}
.btn[data-action="incrementMinutes"]::after {
.sr-only();
content: "Increment Minutes";
}
.btn[data-action="decrementHours"]::after {
.sr-only();
content: "Decrement Hours";
}
.btn[data-action="decrementMinutes"]::after {
.sr-only();
content: "Decrement Minutes";
}
.btn[data-action="showHours"]::after {
.sr-only();
content: "Show Hours";
}
.btn[data-action="showMinutes"]::after {
.sr-only();
content: "Show Minutes";
}
.btn[data-action="togglePeriod"]::after {
.sr-only();
content: "Toggle AM/PM";
}
.btn[data-action="clear"]::after {
.sr-only();
content: "Clear the picker";
}
.btn[data-action="today"]::after {
.sr-only();
content: "Set the date to today";
}
.picker-switch {
text-align: center;
&::after {
.sr-only();
content: "Toggle Date and Time Screens";
}
td {
padding: 0;
margin: 0;
height: auto;
width: auto;
line-height: inherit;
span {
line-height: 2.5;
height: 2.5em;
width: 100%;
}
}
}
table {
width: 100%;
margin: 0;
& td,
& th {
text-align: center;
border-radius: @bs-datetimepicker-border-radius;
}
& th {
height: 20px;
line-height: 20px;
width: 20px;
&.picker-switch {
width: 145px;
}
&.disabled,
&.disabled:hover {
background: none;
color: @bs-datetimepicker-disabled-color;
cursor: not-allowed;
}
&.prev::after {
.sr-only();
content: "Previous Month";
}
&.next::after {
.sr-only();
content: "Next Month";
}
}
& thead tr:first-child th {
cursor: pointer;
&:hover {
background: @bs-datetimepicker-btn-hover-bg;
}
}
& td {
height: 54px;
line-height: 54px;
width: 54px;
&.cw {
font-size: .8em;
height: 20px;
line-height: 20px;
color: @bs-datetimepicker-alternate-color;
}
&.day {
height: 20px;
line-height: 20px;
width: 20px;
}
&.day:hover,
&.hour:hover,
&.minute:hover,
&.second:hover {
background: @bs-datetimepicker-btn-hover-bg;
cursor: pointer;
}
&.old,
&.new {
color: @bs-datetimepicker-alternate-color;
}
&.today {
position: relative;
&:before {
content: '';
display: inline-block;
border: solid transparent;
border-width: 0 0 7px 7px;
border-bottom-color: @bs-datetimepicker-active-bg;
border-top-color: @bs-datetimepicker-secondary-border-color-rgba;
position: absolute;
bottom: 4px;
right: 4px;
}
}
&.active,
&.active:hover {
background-color: @bs-datetimepicker-active-bg;
color: @bs-datetimepicker-active-color;
text-shadow: @bs-datetimepicker-text-shadow;
}
&.active.today:before {
border-bottom-color: #fff;
}
&.disabled,
&.disabled:hover {
background: none;
color: @bs-datetimepicker-disabled-color;
cursor: not-allowed;
}
span {
display: inline-block;
width: 54px;
height: 54px;
line-height: 54px;
margin: 2px 1.5px;
cursor: pointer;
border-radius: @bs-datetimepicker-border-radius;
&:hover {
background: @bs-datetimepicker-btn-hover-bg;
}
&.active {
background-color: @bs-datetimepicker-active-bg;
color: @bs-datetimepicker-active-color;
text-shadow: @bs-datetimepicker-text-shadow;
}
&.old {
color: @bs-datetimepicker-alternate-color;
}
&.disabled,
&.disabled:hover {
background: none;
color: @bs-datetimepicker-disabled-color;
cursor: not-allowed;
}
}
}
}
&.usetwentyfour {
td.hour {
height: 27px;
line-height: 27px;
}
}
&.wider {
width: 21em;
}
& .datepicker-decades .decade {
line-height: 1.8em !important;
}
}
.input-group.date {
& .input-group-addon {
cursor: pointer;
}
}

Просмотреть файл

@ -13,6 +13,27 @@
clear: both;
}
.edit_form__labelHeader{
color: @form-fieldset-textbox-label;
font-size: .6em;
text-transform: uppercase;
}
.values_editor__group input[type=checkbox]{
border: 1px solid @form-textbox-border;
background-color: @form-textbox-background;
margin: .25em 1em 0em 0;
height: 30px;
width: 30px;
}
.values_editor__group td{
text-align:center
}
.values_editor__group>span{
font-size:.70em
}
.edit_form__text {
display: block;
float: left;
@ -26,6 +47,36 @@
box-sizing: content-box;
}
.edit_form__texthalf {
display: block;
float: left;
width: 230px;
margin: .25em 1em 0 0;
border: 1px solid @form-textbox-border;
background-color: @form-textbox-background;
font-size: .85em;
padding: 0 5px;
min-height: 30px;
box-sizing: content-box;
}
.edit_form__textsmall{
display: block;
float: left;
width: 50px;
margin: .25em 0em 1.4em 0;
border: 1px solid @form-textbox-border;
background-color: @form-textbox-background;
font-size: .85em;
padding: 0 5px;
min-height: 30px;
box-sizing: content-box;
}
.non_float{
float:none;
display:inline;
}
.edit_form__text:disabled {
color: @form-textbox-disabled-color;
background-color: @form-textbox-disabled-background;

Просмотреть файл

@ -0,0 +1,17 @@
// Import bootstrap variables including default color palette and fonts
@import "bootstrap/variables.less";
// Import datepicker component
@import "_bootstrap-datetimepicker.less";
//this is here so the compiler doesn't complain about a missing bootstrap mixin
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0,0,0,0);
border: 0;
}

Просмотреть файл

@ -0,0 +1,24 @@
.glyphicon {
position: relative;
top: 1px;
display: inline-block;
font-family: 'Tahoma, Geneva, sans-serif';
font-style: normal;
font-weight: 400;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.glyphicon-calendar {
&:before {
content: "Date";
}
}
.glyphicon-time {
&:before {
content: "Time";
}
}

Просмотреть файл

@ -75,3 +75,7 @@
.button_execute {
clear: both;
}
.button_cancel {
margin-left : .5em;
margin-right : .5em;
}

Просмотреть файл

@ -653,6 +653,24 @@ nav .selected {
display: block;
clear: both;
}
.edit_form__labelHeader {
color: gray;
font-size: 0.6em;
text-transform: uppercase;
}
.values_editor__group input[type=checkbox] {
border: 1px solid #999999;
background-color: #f2f2f2;
margin: 0.25em 1em 0em 0;
height: 30px;
width: 30px;
}
.values_editor__group td {
text-align: center;
}
.values_editor__group > span {
font-size: 0.7em;
}
.edit_form__text {
display: block;
float: left;
@ -665,6 +683,34 @@ nav .selected {
min-height: 30px;
box-sizing: content-box;
}
.edit_form__texthalf {
display: block;
float: left;
width: 230px;
margin: 0.25em 1em 0 0;
border: 1px solid #999999;
background-color: #f2f2f2;
font-size: 0.85em;
padding: 0 5px;
min-height: 30px;
box-sizing: content-box;
}
.edit_form__textsmall {
display: block;
float: left;
width: 50px;
margin: 0.25em 0em 1.4em 0;
border: 1px solid #999999;
background-color: #f2f2f2;
font-size: 0.85em;
padding: 0 5px;
min-height: 30px;
box-sizing: content-box;
}
.non_float {
float: none;
display: inline;
}
.edit_form__text:disabled {
color: #b3b3b3;
background-color: #f2f2f2;
@ -1938,6 +1984,10 @@ table#alertHistoryTable tr.selectedAlert {
.button_execute {
clear: both;
}
.button_cancel {
margin-left: 0.5em;
margin-right: 0.5em;
}
.error_noscript {
background-color: transparent;
color: #ed1c24;
@ -4277,6 +4327,396 @@ tbody.collapse.in {
-webkit-transition-timing-function: ease;
transition-timing-function: ease;
}
/*!
* Datetimepicker for Bootstrap 3
* version : 4.17.43
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/
.bootstrap-datetimepicker-widget {
list-style: none;
}
.bootstrap-datetimepicker-widget.dropdown-menu {
margin: 2px 0;
padding: 4px;
width: 19em;
}
@media (min-width: 768px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
@media (min-width: 992px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
@media (min-width: 1200px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
.bootstrap-datetimepicker-widget.dropdown-menu:before,
.bootstrap-datetimepicker-widget.dropdown-menu:after {
content: '';
display: inline-block;
position: absolute;
}
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #cccccc;
border-bottom-color: rgba(0, 0, 0, 0.2);
top: -7px;
left: 7px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid white;
top: -6px;
left: 8px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.top:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid #cccccc;
border-top-color: rgba(0, 0, 0, 0.2);
bottom: -7px;
left: 6px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.top:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid white;
bottom: -6px;
left: 7px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before {
left: auto;
right: 6px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after {
left: auto;
right: 7px;
}
.bootstrap-datetimepicker-widget .list-unstyled {
margin: 0;
}
.bootstrap-datetimepicker-widget a[data-action] {
padding: 6px 0;
}
.bootstrap-datetimepicker-widget a[data-action]:active {
box-shadow: none;
}
.bootstrap-datetimepicker-widget .timepicker-hour,
.bootstrap-datetimepicker-widget .timepicker-minute,
.bootstrap-datetimepicker-widget .timepicker-second {
width: 54px;
font-weight: bold;
font-size: 1.2em;
margin: 0;
}
.bootstrap-datetimepicker-widget button[data-action] {
padding: 6px;
}
.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Increment Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Increment Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Decrement Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Decrement Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Show Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Show Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Toggle AM/PM";
}
.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Clear the picker";
}
.bootstrap-datetimepicker-widget .btn[data-action="today"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Set the date to today";
}
.bootstrap-datetimepicker-widget .picker-switch {
text-align: center;
}
.bootstrap-datetimepicker-widget .picker-switch::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Toggle Date and Time Screens";
}
.bootstrap-datetimepicker-widget .picker-switch td {
padding: 0;
margin: 0;
height: auto;
width: auto;
line-height: inherit;
}
.bootstrap-datetimepicker-widget .picker-switch td span {
line-height: 2.5;
height: 2.5em;
width: 100%;
}
.bootstrap-datetimepicker-widget table {
width: 100%;
margin: 0;
}
.bootstrap-datetimepicker-widget table td,
.bootstrap-datetimepicker-widget table th {
text-align: center;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget table th {
height: 20px;
line-height: 20px;
width: 20px;
}
.bootstrap-datetimepicker-widget table th.picker-switch {
width: 145px;
}
.bootstrap-datetimepicker-widget table th.disabled,
.bootstrap-datetimepicker-widget table th.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget table th.prev::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Previous Month";
}
.bootstrap-datetimepicker-widget table th.next::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Next Month";
}
.bootstrap-datetimepicker-widget table thead tr:first-child th {
cursor: pointer;
}
.bootstrap-datetimepicker-widget table thead tr:first-child th:hover {
background: #eeeeee;
}
.bootstrap-datetimepicker-widget table td {
height: 54px;
line-height: 54px;
width: 54px;
}
.bootstrap-datetimepicker-widget table td.cw {
font-size: 0.8em;
height: 20px;
line-height: 20px;
color: #777777;
}
.bootstrap-datetimepicker-widget table td.day {
height: 20px;
line-height: 20px;
width: 20px;
}
.bootstrap-datetimepicker-widget table td.day:hover,
.bootstrap-datetimepicker-widget table td.hour:hover,
.bootstrap-datetimepicker-widget table td.minute:hover,
.bootstrap-datetimepicker-widget table td.second:hover {
background: #eeeeee;
cursor: pointer;
}
.bootstrap-datetimepicker-widget table td.old,
.bootstrap-datetimepicker-widget table td.new {
color: #777777;
}
.bootstrap-datetimepicker-widget table td.today {
position: relative;
}
.bootstrap-datetimepicker-widget table td.today:before {
content: '';
display: inline-block;
border: solid transparent;
border-width: 0 0 7px 7px;
border-bottom-color: #337ab7;
border-top-color: rgba(0, 0, 0, 0.2);
position: absolute;
bottom: 4px;
right: 4px;
}
.bootstrap-datetimepicker-widget table td.active,
.bootstrap-datetimepicker-widget table td.active:hover {
background-color: #337ab7;
color: white;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget table td.active.today:before {
border-bottom-color: white;
}
.bootstrap-datetimepicker-widget table td.disabled,
.bootstrap-datetimepicker-widget table td.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget table td span {
display: inline-block;
width: 54px;
height: 54px;
line-height: 54px;
margin: 2px 1.5px;
cursor: pointer;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget table td span:hover {
background: #eeeeee;
}
.bootstrap-datetimepicker-widget table td span.active {
background-color: #337ab7;
color: white;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget table td span.old {
color: #777777;
}
.bootstrap-datetimepicker-widget table td span.disabled,
.bootstrap-datetimepicker-widget table td span.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget.usetwentyfour td.hour {
height: 27px;
line-height: 27px;
}
.bootstrap-datetimepicker-widget.wider {
width: 21em;
}
.bootstrap-datetimepicker-widget .datepicker-decades .decade {
line-height: 1.8em !important;
}
.input-group.date .input-group-addon {
cursor: pointer;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.glyphicon {
position: relative;
top: 1px;
display: inline-block;
font-family: 'Tahoma, Geneva, sans-serif';
font-style: normal;
font-weight: 400;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.glyphicon-calendar:before {
content: "Date";
}
.glyphicon-time:before {
content: "Time";
}
.navbar {
border-radius: 0px;
margin-bottom: 0px;

Просмотреть файл

@ -51,6 +51,8 @@ error and can be *very* helpful.
@import "bootstrap/bootstrap.less";
@import "bootstrap-datetimepicker-build.less";
@import "bootstrap/glyphicon.less";
@import "navbars.less";

Просмотреть файл

@ -4,6 +4,8 @@ using System.Web.Mvc;
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Infrastructure.Repository;
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Models;
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Security;
using Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Models;
using System.Collections.Generic;
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Controllers
{
@ -52,19 +54,37 @@ namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.
}
[RequirePermission(Permission.ManageJobs)]
public async Task<ActionResult> ScheduleTwinUpdate()
public ActionResult ScheduleTwinUpdate()
{
//ToDo: Jump to the view with desired model
return View();
return View(new ScheduleTwinUpdateModel());
}
[RequirePermission(Permission.ManageJobs)]
public async Task<ActionResult> ScheduleDeviceMethod()
[HttpPost]
public ActionResult ScheduleTwinUpdate(ScheduleTwinUpdateModel model)
{
//ToDo: Jump to the view with desired model
return View();
return View(new ScheduleTwinUpdateModel());
}
[RequirePermission(Permission.ManageJobs)]
public ActionResult ScheduleDeviceMethod()
{
//ToDo: Jump to the view with desired model
return View(new ScheduleDeviceMethodModel());
}
[RequirePermission(Permission.ManageJobs)]
[HttpPost]
public ActionResult ScheduleDeviceMethod(ScheduleDeviceMethodModel model)
{
//ToDo: Jump to the view with desired model
return View(new ScheduleDeviceMethodModel());
}
}
}

Просмотреть файл

@ -11,6 +11,7 @@ using System.Web.Routing;
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web
{
using App_Start;
using Helpers;
public class MvcApplication : System.Web.HttpApplication
@ -27,6 +28,8 @@ namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web
// Reset JobList and QueryList via actual current job history for debugging
//RepositoryInitializer.SeedTablesAsync().Wait();
ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder() { TimeFormat = "h:m:ss tt", DateFormat= "MMMM d, yyyy h:mm tt" });
ModelBinders.Binders.Add(typeof(DateTime?), new NullableDateTimeBinder() { TimeFormat = "h:m:ss tt", DateFormat = "MMMM d, yyyy h:mm tt" });
}
// Require HTTPS for all requests processed by ASP.NET

Просмотреть файл

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Models
{
public class MethodParameterEditViewModel
{
public string ParameterName { get; set; }
public string ParameterValue { get; set; }
public string ParameterType { get; set; }
}
public class ScheduleDeviceMethodModel : ScheduleJobViewModel
{
public string MethodName { get; set; }
public List<MethodParameterEditViewModel> Parameters { get; set; }
}
}

Просмотреть файл

@ -1,7 +1,26 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Models
{
public class ScheduleJobViewModel
{
public string QueryName { get; set; }
[Required]
public string JobName { get; set; }
[Required]
public DateTime StartDateUtc { get; set; }
[Required]
public int MaxExecutionTimeInMinutes { get; set; }
}
public class ScheduleJobModel
{
public string QueryName { get; set; }

Просмотреть файл

@ -0,0 +1,34 @@
using GlobalResources;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Models
{
public class DesiredPropetiesEditViewModel
{
public string PropertyName { get; set; }
public string PropertyValue { get; set; }
public string Valuetype { get; set; }
public bool isDeleted { get; set; }
}
public class TagsEditViewModel
{
public string TagName { get; set; }
public string TagValue { get; set; }
public string Valuetype { get; set; }
public bool isDeleted { get; set; }
}
public class ScheduleTwinUpdateModel: ScheduleJobViewModel
{
public List<DesiredPropetiesEditViewModel> DesiredProperties { get; set; }
public List<TagsEditViewModel> Tags { get; set; }
}
}

Просмотреть файл

@ -35,3 +35,43 @@
return parent;
}
}
ko.bindingHandlers.dateTimePicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().dateTimePickerOptions || { };
$(element).datetimepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "dp.change", function (event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
if (event.date != null && !(event.date instanceof Date)) {
value(event.date.toDate());
} else {
value(event.date);
}
}
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
var picker = $(element).data("DateTimePicker");
if (picker) {
picker.destroy();
}
});
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var picker = $(element).data("DateTimePicker");
//when the view model is updated, update the widget
if (picker) {
var koDate = ko.utils.unwrapObservable(valueAccessor());
//in case return from server datetime i am get in this form for example /Date(93989393)/ then fomat this
koDate = (typeof (koDate) !== 'object') ? new Date(parseFloat(koDate.replace(/[^0-9]/g, ''))) : koDate;
picker.date(koDate);
}
}
};

Просмотреть файл

@ -0,0 +1,69 @@
IoTApp.createModule('IoTApp.ScheduleDeviceMethod', function () {
'use strict';
var self = this;
function MethodParameterEditItem(name, type, value) {
var self = this;
self.ParameterName = name;
self.Type = type
self.ParameterValue = value;
}
function viewModel() {
var self = this;
this.queryName = ko.observable("");
this.jobName = ko.observable("");
this.methodName = ko.observable("");
this.methods = {};
this.parameters = ko.pureComputed(function () {
if (self.methodName != undefined
&& self.methodName().length != 0) {
var rawparam = $.grep(self.methods, function (e) { return e.name == self.methodName(); });
if (rawparam.length != 0) {
var params = $.map(rawparam[0].parameters, function (item) {
return new MethodParameterEditItem(item.name, item.type, "")
})
return params.length == 0 ? null : params;
}
return null;
}
}, this);
this.startDate = ko.observable(moment());
this.isParameterLoading = true;
this.maxExecutionTime = ko.observable(30);
this.getMatchedDevices = function (stringtemplate) {
return stringtemplate.replace("{0}", "2").replace("{1}", "5");
}
this.getUnmatchedDevices = function (stringtemplate) {
return stringtemplate.replace("{0}", "3").replace("{1}", "5");
}
this.beforePost = function (elem) {
$(elem).find("#StartDate").val(moment(this.startDate()).utc().format());
return true;
}
this.cacheNameList = function (namelist) {
self.methods = namelist;
IoTApp.Controls.NameSelector.create(jQuery('.name_selector__text'), { type: IoTApp.Controls.NameSelector.NameListType.method }, self.methods);
}
this.init = function () {
IoTApp.Controls.NameSelector.loadNameList({ type: IoTApp.Controls.NameSelector.NameListType.method }, self.cacheNameList)
}
}
var vm = new viewModel();
return {
init: function () {
vm.init();
ko.applyBindings(vm, $("content").get(0));
}
}
}, [jQuery, resources]);

Просмотреть файл

@ -0,0 +1,117 @@

IoTApp.createModule('IoTApp.ScheduleTwinUpdate', function () {
'use strict';
var self = this;
function PropertiesEditItem(name, value, isDeleted) {
var self = this;
self.PropertyName = name;
self.PropertyValue = value;
self.isDeleted = isDeleted;
}
function TagsEditItem(name, value, isDeleted) {
var self = this;
self.TagName = name;
self.TagValue = value;
self.isDeleted = isDeleted;
}
function viewModel() {
var self = this;
this.queryName = ko.observable("");
this.jobName = ko.observable("");
this.properties = ko.observableArray();
this.tags = ko.observableArray();
this.startDate = ko.observable(moment());
this.isPropertiesLoading = true;
this.isTagsLoading = true;
this.maxExecutionTime = ko.observable(30);
this.cachetagList = {};
this.cachepropertyList = {};
this.onepropleft = ko.observable(true);
this.onetagleft = ko.observable(true);
this.propertieslist = {};
this.tagslist = {};
this.createEmptyPropertyIfNeeded = function (property) {
if (self.properties.indexOf(property) == self.properties().length - 1) {
self.properties.push(new PropertiesEditItem("", "", false, false))
self.onepropleft(false);
}
}
this.createEmptyTagIfNeeded = function (tag) {
if (self.tags.indexOf(tag) == self.tags().length - 1) {
self.tags.push(new TagsEditItem("", "", false, false))
self.onetagleft(false);
}
}
this.removeTag = function (tag) {
self.tags.remove(tag);
if (self.tags().length <=1) {
self.onetagleft(true);
}
}
this.removeProperty = function (prop) {
self.properties.remove(prop);
if (self.properties().length <= 1) {
self.onepropleft(true);
}
}
this.beforePost = function (elem) {
$(elem).find("#StartDate").val(moment(this.startDate()).utc().format());
return true;
}
this.maketaglist = function (elem, index, data) {
self.refreshnamecontrol();
}
this.makeproplist = function (elem, index, data) {
self.refreshnamecontrol();
}
this.cachepropertyList = function (namelist) {
self.propertieslist = namelist;
self.refreshnamecontrol();
}
this.cachetagList = function (namelist) {
self.tagslist = namelist;
self.refreshnamecontrol();
}
this.refreshnamecontrol = function () {
jQuery('.edit_form__texthalf.edit_form__propertiesComboBox').each(function () {
IoTApp.Controls.NameSelector.create(jQuery(this), { type: IoTApp.Controls.NameSelector.NameListType.properties }, self.propertieslist);
});
jQuery('.edit_form__texthalf.edit_form__tagsComboBox').each(function () {
IoTApp.Controls.NameSelector.create(jQuery(this), { type: IoTApp.Controls.NameSelector.NameListType.tags }, self.tagslist);
});
}
this.init = function () {
this.properties.push(new PropertiesEditItem("", "", false));
this.tags.push(new TagsEditItem("", "", false));
//IoTApp.Controls.NameSelector.loadNameList({ type: IoTApp.Controls.NameSelector.NameListType.tag }, self.cachetagList);
//IoTApp.Controls.NameSelector.loadNameList({ type: IoTApp.Controls.NameSelector.NameListType.desiredProperty }, self.cachepropertyList);
IoTApp.Controls.NameSelector.loadNameList({ type: IoTApp.Controls.NameSelector.NameListType.method }, self.cachetagList);
IoTApp.Controls.NameSelector.loadNameList({ type: IoTApp.Controls.NameSelector.NameListType.method }, self.cachepropertyList);
}
}
var vm = new viewModel();
return {
init: function () {
vm.init();
ko.applyBindings(vm, $("content").get(0));
}
}
}, [jQuery, resources]);

Просмотреть файл

@ -1,5 +1,6 @@
/// <autosync enabled="true" />
/// <reference path="bootstrap.min.js" />
/// <reference path="bootstrap-datetimepicker.min.js" />
/// <reference path="controls.js" />
/// <reference path="dialog.js" />
/// <reference path="IoTHelperScripts.js" />
@ -43,3 +44,5 @@
/// <reference path="views/devicerules/editdeviceruleproperties.js" />
/// <reference path="views/devicerules/removedevicerule.js" />
/// <reference path="views/iotapp.js" />
/// <reference path="views/job/scheduledevicemethod.js" />
/// <reference path="Views/Job/ScheduleTwinUpdate.js" />

217
DeviceAdministration/Web/Scripts/bootstrap-datetimepicker.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -39,8 +39,8 @@
$element.autocomplete({
source: items,
select: function (event, ui) {
$element.change();
select: function(event,ui){
$(this).val(ui.item.value).change();
},
minLength: 0
}).focus(function () {

Просмотреть файл

@ -0,0 +1,117 @@
@using System.Globalization
@using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Helpers
@using Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Infrastructure.Models
@using Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Helpers
@using GlobalResources
@model Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Models.ScheduleDeviceMethodModel
@{
ViewBag.Title = @Strings.InvokeMethod;
Layout = "~/Views/Shared/_LayoutNoNavigation.cshtml";
}
<header class="header_main">
<button class="header_main__button_back" type="button"></button>
<h2 class="header_main__subhead header_main__subhead--large">@Strings.InvokeMethod <span class="item__description">@Model.QueryName</span></h2>
</header>
<div class="content_outer">
<div class="content_inner">
<div id="content">
<div id="scheduleJobParameter">
@using (Html.BeginForm("ScheduleDeviceMethod", "Job", FormMethod.Post , new { data_bind= "submit: beforePost" }))
{
@Html.AntiForgeryToken()
<p>
@Html.ValidationSummary(false)
</p>
<fieldset class="edit_form">
@{
@Html.LabelFor(m => m.JobName, Strings.ScheduleJobName, new { @class = "edit_form__label" });
@Html.TextBoxFor(m => m.JobName, new { placeholder = Strings.ScheduleJobNamePlaceHolder, data_bind = "value: jobName", @class = "edit_form__text" });
<div style="clear:both"></div>
@Html.LabelFor(m => m.MethodName, Strings.InvokeMethodLabel, new { @class = "edit_form__label" });
@Html.TextBoxFor(m => m.MethodName, new { data_bind = "textInput: methodName, event{ blur: $root.updateParameter }", @class = "name_selector__text edit_form__text" });
<div style="clear:both"></div>
<div>
<span class="text_small" data-bind="text: getMatchedDevices('@Strings.SomeDevicesApplicable')"></span><a href="/Device/Query">query</a>
</div>
<div>
<span class="text_small" data-bind="text: getUnmatchedDevices('@Strings.SomeDevicesApplicable')"></span><a href="/Device/Query">query</a>
</div>
<div style="clear:both"></div>
<label for="parameterTable" class="edit_form__label" data-bind="if: parameters">@Strings.ScheduleParameters</label>
<table class="values_editor" id="paramterTable" data-bind="if: parameters">
<thead>
<tr class="values_editor__groupheader">
<th class="edit_form__labelHeader">@Strings.ScheduleParameterName</th>
<th class="edit_form__labelHeader">@Strings.ScheduleEditValue</th>
</tr>
</thead>
<tbody data-bind="foreach:{data: parameters}">
<tr class="values_editor__group">
<td>
<input type="text" class="edit_form__texthalf"
data-bind="value: ParameterName, attr: { name: 'Parameters[' + $index()+'].ParameterName', id: 'Parameters' + $index()+'.ParameterName' }" />
</td>
<td>
@*data-msg-required="@Strings.RequiredValidationString"
data-rule-required="true"*@
<input type="text" class="edit_form__texthalf"
data-bind="value: ParameterValue, attr: { name: 'Parameters[' + $index()+'].ParameterValue', id: 'Parameters' + $index()+'.ParameterValue' }" />
</td>
</tr>
</tbody>
</table>
<label for="scheduleJobTime" class="edit_form__label" >@Strings.ScheduleSetJobTime</label>
<div id="scheduleJobTime" class="values_editor">
<div class="values_editor__group">
<label class="edit_form__labelHeader">@Strings.StartTime</label>
<input type='text' id="StartDate" name="StartDateUtc" class="edit_form__text non_float" data-bind="dateTimePicker: startDate ,dateTimePickerOptions:{ format: 'LLL' , locale: window.navigator.language}"
data-msg-required="@Strings.RequiredValidationString"
data-rule-required="true" />
</div>
<div class="values_editor__group">
<label class="edit_form__labelHeader">@Strings.MaxExecutionTime</label>
<input type="text" class="edit_form__textsmall non_float" name="MaxExecutionTimeInMinutes" data-bind="value: maxExecutionTime"
data-msg-required="@Strings.RequiredValidationString"
data-rule-required="true" />
<span>Mins</span>
</div>
</div>
}
</fieldset>
<fieldset class="fieldset_button">
<button class="button_base button_secondary button_cancel">@Strings.Cancel</button>
<button class="button_base" type="submit">@Strings.Create</button>
</fieldset>
}
</div>
</div>
</div>
</div>
<script type="text/javascript">
"use strict";
var resources = {
redirectUrl: '@Url.Action("Index", "Device")',
}
</script>
<script type="text/javascript" src="~/Scripts/Views/Job/ScheduleDeviceMethod.js"></script>
<script type="text/javascript">
$(document).ready(function () {
IoTApp.ScheduleDeviceMethod.init();
});
</script>

Просмотреть файл

@ -0,0 +1,143 @@
@using System.Globalization
@using Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Helpers
@using Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Infrastructure.Models
@using Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Helpers
@using GlobalResources
@model Microsoft.Azure.Devices.Applications.RemoteMonitoring.DeviceAdmin.Web.Models.ScheduleTwinUpdateModel
@{
ViewBag.Title = @Strings.UpdateTwin;
Layout = "~/Views/Shared/_LayoutNoNavigation.cshtml";
}
<header class="header_main">
<button class="header_main__button_back" type="button"></button>
<h2 class="header_main__subhead header_main__subhead--large">@Strings.ScheduleEditPropertiesOrTags <span class="item__description">@Model.QueryName</span></h2>
</header>
<div class="content_outer">
<div class="content_inner">
<div id="content">
<div id="scheduleJobParameter">
@using (Html.BeginForm("ScheduleTwinUpdate", "Job", FormMethod.Post,new { data_bind="submit: beforePost" }))
{
@Html.AntiForgeryToken()
<p>
@Html.ValidationSummary(false)
</p>
<fieldset class="edit_form">
@{
@Html.LabelFor(m => m.JobName, Strings.ScheduleJobName, new { @class = "edit_form__label" });
@Html.TextBoxFor(m => m.JobName, new { placeholder = Strings.ScheduleJobNamePlaceHolder, data_bind = "value: jobName", @class = "edit_form__text" });
<div style="clear:both"></div>
<label for="propertiesTable" class="edit_form__label">@Strings.ScheduleChangeDesiredProperties</label>
<table class="values_editor" id="propertiesTable">
<thead>
<tr class="values_editor__groupheader">
<th class="edit_form__labelHeader">@Strings.ScheduleDesiredPropertyName</th>
<th class="edit_form__labelHeader">@Strings.ScheduleEditValue</th>
<th class="edit_form__labelHeader">@Strings.Delete</th>
</tr>
</thead>
<tbody data-bind="foreach:{data: properties, afterAdd:makeproplist}">
<tr class="values_editor__group">
<td>
<input type="text" class="edit_form__texthalf edit_form__propertiesComboBox"
data-bind="value: PropertyName, attr: { name: 'DesiredProperties[' + $index()+'].PropertyName', id: 'DesiredProperties' + $index()+'.PropertyName' }" />
</td>
<td>
@*data-msg-required="@Strings.RequiredValidationString"
data-rule-required="true"*@
<input type="text" class="edit_form__texthalf"
data-bind="value: PropertyValue, event: { blur: $root.createEmptyPropertyIfNeeded }, attr: { name: 'DesiredProperties[' + $index()+'].PropertyValue', id: 'DesiredProperties' + $index()+'.PropertyValue' }" />
</td>
<td>
<input type="checkbox"
value="true"
data-bind="check: isDeleted, attr: { name: 'DesiredProperties[' + $index()+'].isDeleted', id: 'DesiredProperties' + $index()+'.isDeleted' }" />
<input type="hidden" value="false" name="checkbox" data-bind="attr:{ name:'DesiredProperties[' + $index()+'].isDeleted'}" />
</td>
<td><a data-bind="ifnot:$root.onepropleft , click:$root.removeProperty">@Strings.Clear</a></td>
</tr>
</tbody>
</table>
<label for="tagsTable" class="edit_form__label">@Strings.ScheduleChangeTags</label>
<table class="values_editor" id="tagsTable">
<thead>
<tr class="values_editor__groupheader">
<th class="edit_form__labelHeader">@Strings.TagName</th>
<th class="edit_form__labelHeader">@Strings.ScheduleEditValue</th>
<th class="edit_form__labelHeader">@Strings.Delete</th>
</tr>
</thead>
<tbody data-bind="foreach: {data: tags, afterAdd: maketaglist}">
<tr class="values_editor__group">
<td>
<input type="text" class="edit_form__texthalf edit_form__tagsComboBox"
data-bind="value: TagName, attr: { name: 'Tags[' + $index()+'].TagName', id: 'Tags' + $index()+'.TagName' }" />
</td>
<td>
<input type="text" class="edit_form__texthalf"
data-bind="value: TagValue, event: { blur: $root.createEmptyTagIfNeeded },attr: { name: 'Tags[' + $index()+'].TagValue', id: 'Tags' + $index()+'.TagValue' }" />
</td>
<td>
<input type="checkbox"
value="true"
data-bind="check: isDeleted, attr: { name: 'Tags[' + $index()+'].isDeleted', id: 'Tags' + $index()+'.isDeleted' }" />
<input type="hidden" value="false" name="checkbox" data-bind="attr:{ name:'Tags[' + $index()+'].isDeleted'}" />
</td>
<td><a data-bind="ifnot:$root.onetagleft , click:$root.removeTag">@Strings.Clear</a></td>
</tr>
</tbody>
</table>
<label for="scheduleJobTime" class="edit_form__label">@Strings.ScheduleSetJobTime</label>
<div id="scheduleJobTime" class="values_editor">
<div class="values_editor__group">
<label class="edit_form__labelHeader">@Strings.StartTime</label>
<input type='text' id="StartDate" name="StartDateUtc" class="edit_form__text non_float" data-bind="dateTimePicker: startDate ,dateTimePickerOptions:{ format: 'LLL', locale: window.navigator.language}"
data-msg-required="@Strings.RequiredValidationString"
data-rule-required="true" />
</div>
<div class="values_editor__group">
<label class="edit_form__labelHeader">@Strings.MaxExecutionTime</label>
<input type="text" class="edit_form__textsmall non_float" name="MaxExecutionTimeInMinutes" data-bind="value: maxExecutionTime"
data-msg-required="@Strings.RequiredValidationString"
data-rule-required="true" />
<span>Mins</span>
</div>
</div>
}
</fieldset>
<fieldset class="fieldset_button">
<button class="button_base button_secondary button_cancel">@Strings.Cancel</button>
<button class="button_base" type="submit">@Strings.Create</button>
</fieldset>
}
</div>
</div>
</div>
</div>
<script type="text/javascript">
"use strict";
var resources = {
redirectUrl: '@Url.Action("Index", "Device")',
}
</script>
<script type="text/javascript" src="~/Scripts/Views/Job/ScheduleTwinUpdate.js"></script>
<script type="text/javascript">
$(document).ready(function () {
IoTApp.ScheduleTwinUpdate.init();
});
</script>

Просмотреть файл

@ -25,9 +25,11 @@
@Scripts.Render("~/bundles/jquerytable")
@Scripts.Render("~/bundles/jqueryui")
@Scripts.Render("~/bundles/knockout")
@Scripts.Render("~/bundles/bootstrap")
<script src="~/Scripts/moment-with-locales.min.js"></script>
@Scripts.Render("~/bundles/bootstrapdatetime")
<script type="text/javascript" src="~/Scripts/js.cookie-1.5.1.min.js"></script>
<script src="~/Scripts/moment-with-locales.min.js"></script>
<script type="text/javascript" src="~/Scripts/Views/IoTApp.js"></script>
@Styles.Render("~/Content/css/vendor")
@Styles.Render("~/Content/css")
@ -124,6 +126,6 @@
<script src="~/Scripts/controls.js"></script>
@RenderSection("scripts", required: false)
@Scripts.Render("~/bundles/bootstrap")
</body>
</html>

Просмотреть файл

@ -274,6 +274,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>Strings.resx</DependentUpon>
</Compile>
<Compile Include="App_Start\DataBinders.cs" />
<Compile Include="App_Start\Startup.Json.cs" />
<Compile Include="App_Start\Startup.Autofac.cs" />
<Compile Include="App_Start\Startup.WebApi.cs" />
@ -303,10 +304,12 @@
<Compile Include="Models\ActionPropertiesModel.cs" />
<Compile Include="Models\AlertHistoryDeviceModel.cs" />
<Compile Include="Models\EditDeviceRuleModel.cs" />
<Compile Include="Models\ScheduleDeviceMethodModel.cs" />
<Compile Include="Models\LanguageModel.cs" />
<Compile Include="Models\NamedDeviceJob.cs" />
<Compile Include="Models\NamedJobResponseModel.cs" />
<Compile Include="Models\ScheduleJobModel.cs" />
<Compile Include="Models\ScheduleTwinUpdateModel.cs" />
<Compile Include="Models\SimInformationViewModel.cs" />
<Compile Include="Models\UpdateActionModel.cs" />
<Compile Include="Models\AlertHistoryResultsModel.cs" />
@ -416,6 +419,9 @@
<Content Include="Content\img\sort_desc.png" />
<Content Include="Content\img\sort_desc_disabled.png" />
<Content Include="Content\styles\main.css" />
<Content Include="Content\styles\main.min.css">
<DependentUpon>main.css</DependentUpon>
</Content>
<Content Include="Content\styles\visuals.min.css" />
<Content Include="Content\styles\bootstrap\dropdowns.less" />
<Content Include="Content\styles\bootstrap\bootstrap.less" />
@ -458,7 +464,11 @@
<Content Include="Content\styles\bootstrap\mixins\text-emphasis.less" />
<Content Include="Content\styles\bootstrap\mixins\text-overflow.less" />
<Content Include="Content\styles\bootstrap\mixins\vendor-prefixes.less" />
<Content Include="Content\styles\bootstrap-datetimepicker-build.less" />
<Content Include="Content\styles\_bootstrap-datetimepicker.less" />
<Content Include="Content\styles\bootstrap\glyphicon.less" />
<None Include="Scripts\jquery-2.2.3.intellisense.js" />
<Content Include="Scripts\bootstrap-datetimepicker.min.js" />
<Content Include="Scripts\bootstrap.min.js" />
<Content Include="Scripts\controls.js" />
<Content Include="Scripts\jquery-2.2.3.js" />
@ -571,6 +581,8 @@
<Content Include="Scripts\Views\Device\EditDeviceProperties.js" />
<Content Include="Scripts\Views\Device\RemoveDevice.js" />
<Content Include="Scripts\Views\IoTApp.js" />
<Content Include="Scripts\Views\Job\ScheduleDeviceMethod.js" />
<Content Include="Scripts\Views\Job\ScheduleTwinUpdate.js" />
<Content Include="Scripts\_references.js" />
<Content Include="Scripts\jquery.dataTables.min.js" />
<Content Include="Scripts\jquery.unobtrusive-ajax.js" />
@ -653,6 +665,8 @@
<Content Include="Views\Advanced\_IccidAssociation.cshtml" />
<Content Include="Views\Job\_ScheduleJob.cshtml" />
<Content Include="Views\Device\_DeviceListColumns.cshtml" />
<Content Include="Views\Job\ScheduleTwinUpdate.cshtml" />
<Content Include="Views\Job\ScheduleDeviceMethod.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="packages.config">
@ -783,9 +797,11 @@
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.Azure.DocumentDB.1.6.3\build\Microsoft.Azure.DocumentDB.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Azure.DocumentDB.1.6.3\build\Microsoft.Azure.DocumentDB.targets'))" />
<Error Condition="!Exists('..\..\packages\BuildWebCompiler.1.11.319\build\BuildWebCompiler.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\BuildWebCompiler.1.11.319\build\BuildWebCompiler.targets'))" />
</Target>
<Import Project="..\..\packages\Microsoft.Web.WebJobs.Publish.1.0.11\tools\webjobs.targets" Condition="Exists('..\..\packages\Microsoft.Web.WebJobs.Publish.1.0.11\tools\webjobs.targets')" />
<Import Project="..\..\packages\Microsoft.Azure.DocumentDB.1.6.3\build\Microsoft.Azure.DocumentDB.targets" Condition="Exists('..\..\packages\Microsoft.Azure.DocumentDB.1.6.3\build\Microsoft.Azure.DocumentDB.targets')" />
<Import Project="..\..\packages\BuildWebCompiler.1.11.319\build\BuildWebCompiler.targets" Condition="Exists('..\..\packages\BuildWebCompiler.1.11.319\build\BuildWebCompiler.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

Просмотреть файл

@ -6,6 +6,7 @@
<package id="Autofac.Owin" version="3.1.0" targetFramework="net451" />
<package id="Autofac.WebApi2" version="3.4.0" targetFramework="net451" />
<package id="Autofac.WebApi2.Owin" version="3.3.0" targetFramework="net451" />
<package id="BuildWebCompiler" version="1.11.319" targetFramework="net451" />
<package id="dotless" version="1.5.2" targetFramework="net451" />
<package id="DotNetty.Buffers-signed" version="0.3.1" targetFramework="net451" />
<package id="DotNetty.Codecs.Mqtt-signed" version="0.3.1" targetFramework="net451" />