diff --git a/rudder-core/src/main/scala/com/normation/rudder/repository/NodeRepository.scala b/rudder-core/src/main/scala/com/normation/rudder/repository/NodeRepository.scala index c277ea6ff3..e9ea46a9ff 100644 --- a/rudder-core/src/main/scala/com/normation/rudder/repository/NodeRepository.scala +++ b/rudder-core/src/main/scala/com/normation/rudder/repository/NodeRepository.scala @@ -44,6 +44,7 @@ import com.normation.rudder.reports.AgentRunInterval import com.normation.rudder.reports.HeartbeatConfiguration import net.liftweb.common._ import com.normation.inventory.domain.NodeId +import com.normation.rudder.domain.policies.PolicyMode /** * Node Repository diff --git a/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPNodeRepository.scala b/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPNodeRepository.scala index 83d6d6949f..ef7a69ef7c 100644 --- a/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPNodeRepository.scala +++ b/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPNodeRepository.scala @@ -50,6 +50,7 @@ import com.normation.rudder.reports.AgentRunInterval import com.normation.rudder.reports.HeartbeatConfiguration import net.liftweb.common._ import com.normation.rudder.repository.EventLogRepository +import com.normation.rudder.domain.policies.PolicyMode class WoLDAPNodeRepository( nodeDit : NodeDit diff --git a/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala b/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala index ba0d1d4ed4..690b3d21be 100644 --- a/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala +++ b/rudder-web/src/main/scala/bootstrap/liftweb/AppConfig.scala @@ -387,7 +387,7 @@ object RudderConfig extends Loggable { val reportsRepository : ReportsRepository = reportsRepositoryImpl val eventLogDeploymentService: EventLogDeploymentService = eventLogDeploymentServiceImpl val allBootstrapChecks : BootstrapChecks = allChecks - lazy val srvGrid = new SrvGrid(roAgentRunsRepository, asyncComplianceService) + lazy val srvGrid = new SrvGrid(roAgentRunsRepository, asyncComplianceService, configService) val findExpectedReportRepository : FindExpectedReportRepository = findExpectedRepo val historizationRepository : HistorizationRepository = historizationJdbcRepository val roApiAccountRepository : RoApiAccountRepository = roLDAPApiAccountRepository @@ -1117,7 +1117,7 @@ object RudderConfig extends Loggable { , diffRepos , PendingInventory ) - private[this] lazy val nodeGridImpl = new NodeGrid(ldapFullInventoryRepository, nodeInfoServiceImpl) + private[this] lazy val nodeGridImpl = new NodeGrid(ldapFullInventoryRepository, nodeInfoServiceImpl, configService) private[this] lazy val modificationService = new ModificationService(logRepository,gitModificationRepository,itemArchiveManagerImpl,uuidGen) private[this] lazy val eventListDisplayerImpl = new EventListDisplayer( @@ -1748,7 +1748,9 @@ object RudderConfig extends Loggable { roLdapRuleRepository , roLdapDirectiveRepository , reportingServiceImpl - , techniqueRepositoryImpl) + , techniqueRepositoryImpl + , configService + ) private[this] lazy val propertyRepository = new RudderPropertiesSquerylRepository( squerylDatasourceProvider , reportsRepository ) diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/components/AgentPolicyModeEditForm.scala b/rudder-web/src/main/scala/com/normation/rudder/web/components/AgentPolicyModeEditForm.scala new file mode 100644 index 0000000000..2cdebe7991 --- /dev/null +++ b/rudder-web/src/main/scala/com/normation/rudder/web/components/AgentPolicyModeEditForm.scala @@ -0,0 +1,33 @@ +package com.normation.rudder.web.components + +import net.liftweb.http.DispatchSnippet +import net.liftweb.common._ +import net.liftweb.http.{SHtml,S} +import scala.xml._ +import net.liftweb.http.js._ +import JsCmds._ +import JE._ +import net.liftweb.util.Helpers +import net.liftweb.util.Helpers._ +import net.liftweb.http.Templates + +class AgentPolicyModeEditForm extends DispatchSnippet with Loggable { + + // Html template + def templatePath = List("templates-hidden", "components", "ComponentAgentPolicyMode") + def template() = Templates(templatePath) match { + case Empty | Failure(_,_,_) => + sys.error("Template for Agent Policy Mode configuration not found. I was looking for %s.html" + .format(templatePath.mkString("/"))) + case Full(n) => n + } + def agentPolicyModeTemplate = chooseTemplate("agentpolicymode", "form", template) + + def dispatch = { + case "cfagentPolicyModeConfiguration" => (xml) => cfagentPolicyModeConfiguration + } + + def cfagentPolicyModeConfiguration = { + agentPolicyModeTemplate ++ Script(OnLoad(JsRaw("angular.bootstrap('#auditMode', ['auditmode']);"))) + } +} diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/components/ComplianceModeEditForm.scala b/rudder-web/src/main/scala/com/normation/rudder/web/components/ComplianceModeEditForm.scala index 1c92a7ebf8..98a2f32d29 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/components/ComplianceModeEditForm.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/components/ComplianceModeEditForm.scala @@ -212,9 +212,9 @@ class ComplianceModeEditForm [T <: ComplianceMode] ( s""" angular.bootstrap("#complianceMode", ['complianceMode']); var scope = angular.element($$("#complianceModeController")).scope(); - scope.$$apply(function(){ - scope.init(${toJs(complianceMode)}, ${toJs(globalMode)}, ${isNodePage} ,${callback.toJsCmd}, "${S.contextPath}", ${allModes}); - } ); + scope.$$apply(function(){ + scope.init(${toJs(complianceMode)}, ${toJs(globalMode)}, ${isNodePage} ,${callback.toJsCmd}, "${S.contextPath}", ${allModes}); + }); """ }) match { case eb:EmptyBox => diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/components/DirectiveEditForm.scala b/rudder-web/src/main/scala/com/normation/rudder/web/components/DirectiveEditForm.scala index 81b564e398..9cf538ceaf 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/components/DirectiveEditForm.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/components/DirectiveEditForm.scala @@ -68,6 +68,10 @@ import com.normation.cfclerk.domain.TechniqueName import com.normation.rudder.web.components.popup.ModificationValidationPopup import com.normation.cfclerk.domain.TechniqueId import com.normation.cfclerk.domain.TechniqueVersion +import com.normation.rudder.web.rest.node.SettingsAPI8 +import com.normation.rudder.web.rest.node.SettingsApi +import com.normation.rudder.domain.policies.PolicyMode.Verify +import com.normation.rudder.domain.policies.PolicyMode.Enforce object DirectiveEditForm { @@ -100,17 +104,18 @@ object DirectiveEditForm { * Parameters can not be null. */ class DirectiveEditForm( - htmlId_policyConf : String - , technique : Technique - , activeTechnique : ActiveTechnique - , fullActiveTechnique : FullActiveTechnique - , val directive : Directive - , oldDirective : Option[Directive] - , workflowEnabled : Boolean - , onSuccessCallback : (Either[Directive,ChangeRequestId]) => JsCmd = { (Directive) => Noop } - , onMigrationCallback : (Directive, Option[Directive]) => JsCmd - , onFailureCallback : () => JsCmd = { () => Noop } - , isADirectiveCreation : Boolean = false + htmlId_policyConf : String + , technique : Technique + , activeTechnique : ActiveTechnique + , fullActiveTechnique : FullActiveTechnique + , val directive : Directive + , oldDirective : Option[Directive] + , workflowEnabled : Boolean + , globalMode : GlobalPolicyMode + , onSuccessCallback : (Either[Directive,ChangeRequestId]) => JsCmd = { (Directive) => Noop } + , onMigrationCallback : (Directive, Option[Directive]) => JsCmd + , onFailureCallback : () => JsCmd = { () => Noop } + , isADirectiveCreation : Boolean = false , onRemoveSuccessCallBack : () => JsCmd = { () => Noop } ) extends DispatchSnippet with Loggable { @@ -122,8 +127,9 @@ class DirectiveEditForm( private[this] val woChangeRequestRepo = RudderConfig.woChangeRequestRepository private[this] val roChangeRequestRepo = RudderConfig.roChangeRequestRepository private[this] val techniqueRepo = RudderConfig.techniqueRepository - private[this] val roRuleRepo = RudderConfig.roRuleRepository - private[this] val roRuleCategoryRepo = RudderConfig.roRuleCategoryRepository + private[this] val roRuleRepo = RudderConfig.roRuleRepository + private[this] val roRuleCategoryRepo = RudderConfig.roRuleCategoryRepository + private[this] val configService = RudderConfig.configService private[this] val htmlId_save = htmlId_policyConf + "Save" private[this] val parameterEditor = { @@ -131,12 +137,12 @@ class DirectiveEditForm( case Full(pe) => pe case Empty => { val errMsg = "Can not initialize the parameter editor for Directive %s " + - "(template %s). No error returned" + "(template %s). No error returned" throw new IllegalArgumentException(errMsg.format(directive.id, technique.id)) } case Failure(m, _, _) => { val errMsg = "Can not initialize the parameter editor for Directive %s " + - "(template %s). Error message: %s" + "(template %s). Error message: %s" throw new IllegalArgumentException(errMsg.format(directive.id, technique.id, m)) } } @@ -207,7 +213,7 @@ class DirectiveEditForm( val agentCompEmpty : NodeSeq = Can be used on any agent. - + technique.compatible match { case None => (osCompEmpty,agentCompEmpty) @@ -226,7 +232,7 @@ class DirectiveEditForm( case agent => {agent.mkString(", ")} - + } (osComp,agentComp) } @@ -272,6 +278,7 @@ class DirectiveEditForm( "#shortDescriptionField" #> piShortDescription.toForm_! & "#longDescriptionField" #> piLongDescription.toForm_! & "#priority" #> piPriority.toForm_! & + "#policyModes" #> policyModes.toForm_! & "#version" #> versionSelect.toForm_! & "#migrate" #> migrateButton(directiveVersion.is,"Migrate") & "#parameters" #> parameterEditor.toFormNodeSeq & @@ -396,28 +403,25 @@ class DirectiveEditForm( private[this] val piPriority = new WBSelectObjField( - "Priority:" + "Priority" , (0 to 10).map(i => (i, i.toString)) , defaultValue = directive.priority ) { override val displayHtml =
- Priority: - - -
-

Priority

- Priority determines which unique Directive will be applied. -
- Unique Directives can be applied only once (ie. Time Settings), so only the highest priority will be appllied. -
- Highest Priority is 0 -
-
-
+ Priority + + +
+

Priority

+

Priority determines which unique Directive will be applied.

+

Unique Directives can be applied only once (ie. Time Settings), so only the highest priority will be appllied.

+

Highest Priority is 0.

+
+
+ override def className = "twoCol" override def labelClassName = "threeCol directiveInfo" - } def showDeprecatedVersion (version : TechniqueVersion) = { @@ -427,6 +431,55 @@ class DirectiveEditForm( } s"${version} ${deprecationInfo}" } + private[this] val globalOverrideText = configService.rudder_policy_overridable().getOrElse("") match { + case true => +
+ You may override the agent policy mode on this directive. + If set to Audit this directive will never be enforced. + If set to Enforce this directive will appply necessary changes except on Nodes with a Verify override setting. +
+ case false => +

+ Currrent global settings do not allow this mode to be overriden on a per-directive bases. You may change this in Settings, + or contact your Rudder administrator about this. +

+ } + private[this] val policyModes = { + val globalPolicyModeName = configService.rudder_policy_mode_name().getOrElse("error").toString + val policyName = directive.policyMode match { + case None => globalPolicyModeName + case _ => directive.policyMode.getOrElse("error").toString + } + val l = Seq(None -> s"Use global default mode (currently ${globalPolicyModeName})", Some(Enforce) -> "Override to Enforce", Some(Verify) -> "Override to Audit") + new WBSelectObjField( + "Policy mode" + , l + , defaultValue = directive.policyMode + ) { + override val displayHtml = +
+ Policy mode + + +
+

Policy mode

+

Configuration rules in Rudder can operate in one of two modes:

+
    +
  1. Audit: the agent will examine configurations and report any differences, but will not make any changes
  2. +
  3. Enforce: the agent will make changes to fix any configurations that differ from your directives
  4. +
+

+ By default all nodes and all directives operate in the global default mode defined in + Settings, which is currently {globalPolicyModeName}. +

+ {globalOverrideText} +
+
+
+ override def className = "twoCol" + override def labelClassName = "threeCol directiveInfo" + } + } val versions = fullActiveTechnique.techniques.keys.map(v => (v,showDeprecatedVersion(v))).toSeq.sortBy(_._1) @@ -487,12 +540,13 @@ class DirectiveEditForm( } val newDirective = directive.copy( - parameters = parameterEditor.mapValueSeq - , name = piName.is + parameters = parameterEditor.mapValueSeq + , name = piName.is , shortDescription = piShortDescription.is - , priority = piPriority.is - , longDescription = piLongDescription.is - , _isEnabled = directive.isEnabled + , priority = piPriority.is + , longDescription = piLongDescription.is + , _isEnabled = directive.isEnabled + , policyMode = policyModes.is ) displayConfirmationPopup( @@ -506,11 +560,13 @@ class DirectiveEditForm( val isMigration = oldDirective.map( _.techniqueVersion != directive.techniqueVersion).getOrElse(false) val updatedDirective = directive.copy( - parameters = parameterEditor.mapValueSeq, - name = piName.is, - shortDescription = piShortDescription.is, - priority = piPriority.is, - longDescription = piLongDescription.is) + parameters = parameterEditor.mapValueSeq + , name = piName.is + , shortDescription = piShortDescription.is + , priority = piPriority.is + , longDescription = piLongDescription.is + , policyMode = policyModes.is + ) if ((!isMigration && directive == updatedDirective && updatedRules.isEmpty)) { onNothingToDo() diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/components/RuleCompliance.scala b/rudder-web/src/main/scala/com/normation/rudder/web/components/RuleCompliance.scala index 5b06a261be..31a357f36f 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/components/RuleCompliance.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/components/RuleCompliance.scala @@ -89,6 +89,7 @@ class RuleCompliance ( private[this] val reportingService = RudderConfig.reportingService private[this] val recentChangesService = RudderConfig.recentChangesService private[this] val categoryService = RudderConfig.ruleCategoryService + private[this] val configService = RudderConfig.configService //fresh value when refresh private[this] val roRuleRepository = RudderConfig.roRuleRepository @@ -154,7 +155,7 @@ class RuleCompliance ( function refresh() {${refresh().toJsCmd}}; createDirectiveTable(true, false, "${S.contextPath}")("reportsGrid",[],refresh); createNodeComplianceTable("nodeReportsGrid",[],"${S.contextPath}", refresh); - createChangesTable("changesGrid",[],"${S.contextPath}"); + createChangesTable("changesGrid",[],"${S.contextPath}", refresh); correctButtons(); refresh(); """))) @@ -293,10 +294,11 @@ class RuleCompliance ( updatedRule <- roRuleRepository.get(rule.id) directiveLib <- getFullDirectiveLib() allNodeInfos <- getAllNodeInfos() + globalMode <- configService.rudder_global_policy_mode() } yield { - val directiveData = ComplianceData.getRuleByDirectivesComplianceDetails(reports, updatedRule, allNodeInfos, directiveLib).json.toJsCmd - val nodeData = ComplianceData.getRuleByNodeComplianceDetails(directiveLib, reports, allNodeInfos).json.toJsCmd + val directiveData = ComplianceData.getRuleByDirectivesComplianceDetails(reports, updatedRule, allNodeInfos, directiveLib, globalMode).json.toJsCmd + val nodeData = ComplianceData.getRuleByNodeComplianceDetails(directiveLib, reports, allNodeInfos, globalMode).json.toJsCmd JsRaw(s""" refreshTable("reportsGrid", ${directiveData}); refreshTable("nodeReportsGrid", ${nodeData}); diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/components/RuleEditForm.scala b/rudder-web/src/main/scala/com/normation/rudder/web/components/RuleEditForm.scala index 9c9ca14d81..2af2bc2b0e 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/components/RuleEditForm.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/components/RuleEditForm.scala @@ -90,6 +90,8 @@ import com.normation.rudder.rule.category.RoRuleCategoryRepository import com.normation.rudder.rule.category.RuleCategoryId import com.normation.rudder.web.model.JsInitContextLinkUtil import com.normation.rudder.rule.category.RuleCategory +import com.normation.rudder.reports.GlobalComplianceMode +import com.normation.rudder.domain.policies.GlobalPolicyMode object RuleEditForm { @@ -168,6 +170,7 @@ class RuleEditForm( private[this] val getFullDirectiveLib = RudderConfig.roDirectiveRepository.getFullDirectiveLibrary _ private[this] val getAllNodeInfos = RudderConfig.nodeInfoService.getAll _ private[this] val getRootRuleCategory = RudderConfig.roRuleCategoryRepository.getRootCategory _ + private[this] val configService = RudderConfig.configService private[this] val usedDirectiveIds = roRuleRepository.getAll().getOrElse(Seq()).flatMap { case r => r.directiveIds.map( id => (id -> r.id)) @@ -185,13 +188,13 @@ class RuleEditForm( private[this] val boxRootRuleCategory = getRootRuleCategory() private[this] def showForm(tab :Int = 0, idToScroll : String = "editRuleZonePortlet") : NodeSeq = { - (getFullNodeGroupLib(), getFullDirectiveLib(), getAllNodeInfos(), boxRootRuleCategory) match { - case (Full(groupLib), Full(directiveLib), Full(nodeInfos), Full(rootRuleCategory)) => + (getFullNodeGroupLib(), getFullDirectiveLib(), getAllNodeInfos(), boxRootRuleCategory, configService.rudder_global_policy_mode()) match { + case (Full(groupLib), Full(directiveLib), Full(nodeInfos), Full(rootRuleCategory), Full(globalMode)) => val form = { if(CurrentUser.checkRights(Read("rule"))) { val formContent = if (CurrentUser.checkRights(Read("rule"))) { - showCrForm(groupLib, directiveLib) + showCrForm(groupLib, directiveLib, globalMode) } else {
You have no rights to see rules details, please contact your administrator
} @@ -235,8 +238,8 @@ class RuleEditForm( ) ) - case (groupLib, directiveLib, allNodes, rootRuleCategory) => - List(groupLib, directiveLib, allNodes, rootRuleCategory).collect{ case eb: EmptyBox => + case (groupLib, directiveLib, allNodes, rootRuleCategory, globalMode) => + List(groupLib, directiveLib, allNodes, rootRuleCategory, globalMode).collect{ case eb: EmptyBox => val e = eb ?~! "An error happens when trying to get rule datas" logger.error(e.messageChain)
{e.msg}
@@ -261,7 +264,7 @@ class RuleEditForm( } - private[this] def showCrForm(groupLib: FullNodeGroupCategory, directiveLib: FullActiveTechniqueCategory) : NodeSeq = { + private[this] def showCrForm(groupLib: FullNodeGroupCategory, directiveLib: FullActiveTechniqueCategory, globalMode : GlobalPolicyMode) : NodeSeq = { //is't there an other way to do that? We already have the target/name //in the tree, so there's just the existing id to find back @@ -294,7 +297,8 @@ class RuleEditForm(
{
- {showExtraContent(node, sm, salt)} + {showExtraContent(nodeAndGlobalMode.map(_._1), sm, salt)}
- {showNodeDetails(sm, None, inventoryStatus, salt)} + {showNodeDetails(sm, nodeAndGlobalMode, None, inventoryStatus, salt)}
} // mimic the content of server_details/ShowNodeDetailsFromNode - def showNodeDetails(sm:FullInventory, creationDate:Option[DateTime], inventoryStatus : InventoryStatus, salt:String = "", isDisplayingInPopup:Boolean = false) : NodeSeq = { + def showNodeDetails( + sm : FullInventory + , nodeAndGlobalMode : Option[(NodeInfo,GlobalPolicyMode)] + , creationDate : Option[DateTime] + , inventoryStatus : InventoryStatus + , salt : String = "" + , isDisplayingInPopup: Boolean = false + ) : NodeSeq = { + val nodePolicyMode = nodeAndGlobalMode match { + case Some((node,globalMode)) => + Some((globalMode.overridable,node.policyMode) match { + case (Always, Some(mode)) => + (mode,"

This mode is an override applied to this node. You can change it in the node settings.

") + case (Always,None) => + val expl = """

This mode is the globally defined default. You can change it in settings.

You can also override it on this node in the node's settings.

""" + (globalMode.mode, expl) + case (Unoverridable,_) => + (globalMode.mode, "

This mode is the globally defined default. You can change it in Settings.

") + } + ) + + case None => + None + } val deleteButton : NodeSeq= { sm.node.main.status match { @@ -366,6 +398,12 @@ $$("#${detailsId}").bind( "show", function(event, ui) {

Rudder information

+ { nodePolicyMode match { + case None => NodeSeq.Empty + case Some((mode,explanation)) => + Agent policy mode :
++ + Script(OnLoad(JsRaw(s"""$$('#badge-apm').append(createBadgeAgentPolicyMode('node',"${mode}","${explanation}"));$$('.rudder-label').bsTooltip();"""))) + } } { displayServerRole(sm, inventoryStatus) } Inventory date: {sm.node.inventoryDate.map(DateFormaterService.getFormatedDate(_)).getOrElse("Unknown")}
Date inventory last received: {sm.node.receiveDate.map(DateFormaterService.getFormatedDate(_)).getOrElse("Unknown")}
@@ -393,10 +431,10 @@ $$("#${detailsId}").bind( "show", function(event, ui) { case _ => NodeSeq.Empty } - val publicKeyId = s"publicKey-${sm.node.main.id.value}" + val publicKeyId = s"publicKey-${sm.node.main.id.value}" Display Node key {checked}
++ - Script(OnLoad(JsRaw("""createTooltip();"""))) + Script(OnLoad(JsRaw(s"""createTooltip();"""))) case None => NodeSeq.Empty } } @@ -441,7 +479,6 @@ $$("#${detailsId}").bind( "show", function(event, ui) { } } } - val roles = if (nodeInfo.serverRoles.isEmpty) { "" } else { diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/services/DisplayNodeGroupTree.scala b/rudder-web/src/main/scala/com/normation/rudder/web/services/DisplayNodeGroupTree.scala index 0c353686c6..c09d04007b 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/services/DisplayNodeGroupTree.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/services/DisplayNodeGroupTree.scala @@ -163,9 +163,9 @@ object DisplayNodeGroupTree extends Loggable { val tooltipId = Helpers.nextFuncName -
Configure this group.
+ onclick={redirectToGroupLink(NodeGroupId(groupId)).toJsCmd} + >
+
Configure this group.
} else { NodeSeq.Empty @@ -197,6 +197,7 @@ object DisplayNodeGroupTree extends Loggable { NodeSeq.Empty } } + val xml = { val tooltipId = Helpers.nextFuncName diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/services/NodeGrid.scala b/rudder-web/src/main/scala/com/normation/rudder/web/services/NodeGrid.scala index 68ea00bc93..25a910503b 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/services/NodeGrid.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/services/NodeGrid.scala @@ -64,6 +64,7 @@ import net.liftweb.http.Templates import com.normation.rudder.domain.servers.Srv import com.normation.utils.HashcodeCaching import com.normation.rudder.services.nodes.NodeInfoService +import com.normation.rudder.appconfig.ReadConfigService object NodeGrid { val logger = LoggerFactory.getLogger(classOf[NodeGrid]) @@ -87,6 +88,7 @@ case class JsonArg(jsid:String, id:String, status:String) extends HashcodeCachin class NodeGrid( getNodeAndMachine: LDAPFullInventoryRepository , nodeInfoService : NodeInfoService + , configService : ReadConfigService ) extends Loggable { private def templatePath = List("templates-hidden", "server_grid") @@ -257,11 +259,25 @@ class NodeGrid( status : InventoryStatus <- Box(InventoryStatus(arg.status)) nodeId = NodeId(arg.id) sm <- getNodeAndMachine.get(nodeId, status) - } yield (nodeId, sm, arg.jsid, status) ) match { - case Full((nodeId, sm, jsid, status)) => + nodeAndGlobalMode <- { + nodeInfoService.getNodeInfo(nodeId) match { + case Full(Some(node)) => + configService.rudder_global_policy_mode() match { + case Full(mode) => Full(Some(node,mode)) + case eb : EmptyBox => + val fail = eb ?~! s" Could not get global policy mode when getting node '${nodeId}' details" + fail + } + case eb : EmptyBox => + val fail = eb ?~! s" Error when getting node '${nodeId}' details" + fail + case Full(None) => Full(None) + } + } + } yield (nodeId, sm, arg.jsid, status, nodeAndGlobalMode) ) match { + case Full((nodeId, sm, jsid, status, nodeAndGlobalMode)) => // Node may not be available, so we look for it outside the for comprehension - val node = nodeInfoService.getNodeInfo(nodeId).openOr(None) - SetHtml(jsid, DisplayNode.showPannedContent(node, sm, status)) & + SetHtml(jsid, DisplayNode.showPannedContent(nodeAndGlobalMode, sm, status)) & DisplayNode.jsInit(sm.node.main.id, sm.node.softwareIds, "") case e:EmptyBox => logger.debug((e ?~! "error").messageChain) diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/services/ReportDisplayer.scala b/rudder-web/src/main/scala/com/normation/rudder/web/services/ReportDisplayer.scala index faec99314c..594fd0050f 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/services/ReportDisplayer.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/services/ReportDisplayer.scala @@ -64,6 +64,9 @@ import com.normation.rudder.web.model.JsNodeId import com.normation.rudder.services.reports._ import com.normation.rudder.repository.NodeConfigIdInfo import org.joda.time.format.DateTimeFormat +import com.normation.rudder.domain.policies.GlobalPolicyMode +import com.normation.rudder.appconfig.ConfigRepository +import com.normation.rudder.appconfig.ReadConfigService /** * Display the last reports of a server @@ -74,10 +77,10 @@ class ReportDisplayer( , directiveRepository : RoDirectiveRepository , reportingService : ReportingService , techniqueRepository : TechniqueRepository + , configService : ReadConfigService ) extends Loggable { private[this] val getAllNodeInfos = RudderConfig.nodeInfoService.getAll _ - private[this] val templateByNodePath = List("templates-hidden", "reports_server") private def templateByNode() = Templates(templateByNodePath) match { case Empty | Failure(_,_,_) => @@ -98,13 +101,7 @@ class ReportDisplayer( def asyncDisplay(node : NodeInfo) : NodeSeq = { val id = JsNodeId(node.id) val callback = SHtml.ajaxInvoke(() => SetHtml("reportsDetails",displayReports(node)) ) - Script(OnLoad(JsRaw( - s""" - $$("#details_${id}").bind( "show", function(event, ui) { - if(ui.panel.id== 'node_reports') { ${callback.toJsCmd} } - }); - """ - ))) + Script(OnLoad(JsRaw(s"""${callback.toJsCmd}"""))) } /** @@ -344,6 +341,7 @@ class ReportDisplayer( private[this] def showReportDetail(reports: NodeStatusReport, node: NodeInfo, withCompliance: Boolean): NodeSeq = { val data = getComplianceData(node.id, reports).map(_.json).getOrElse(JsArray()) + val configService = RudderConfig.configService val jsFunctionName = if(withCompliance) { "createRuleComplianceTable" @@ -363,8 +361,9 @@ class ReportDisplayer( directiveLib <- directiveRepository.getFullDirectiveLibrary allNodeInfos <- getAllNodeInfos() rules <- ruleRepository.getAll(true) + globalMode <- configService.rudder_global_policy_mode() } yield { - ComplianceData.getNodeByRuleComplianceDetails(nodeId, reportStatus, allNodeInfos, directiveLib, rules) + ComplianceData.getNodeByRuleComplianceDetails(nodeId, reportStatus, allNodeInfos, directiveLib, rules, globalMode) } } diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/services/SrvGrid.scala b/rudder-web/src/main/scala/com/normation/rudder/web/services/SrvGrid.scala index 13d4e3b030..13ebc04d42 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/services/SrvGrid.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/services/SrvGrid.scala @@ -63,6 +63,12 @@ import com.normation.rudder.reports.execution.AgentRun import com.normation.rudder.domain.logger.TimingDebugLogger import com.normation.inventory.domain.VirtualMachineType import com.normation.inventory.domain.PhysicalMachineType +import com.normation.rudder.domain.policies.GlobalPolicyMode +import com.normation.rudder.domain.policies.PolicyModeOverrides.Always +import com.normation.rudder.domain.policies.PolicyModeOverrides.Unoverridable +import com.normation.rudder.appconfig.ReadConfigService +import com.normation.rudder.domain.policies.PolicyMode.Enforce +import com.normation.rudder.domain.policies.PolicyMode.Verify /** * Very much like the NodeGrid, but with the new WB and without ldap information @@ -85,7 +91,8 @@ object SrvGrid { */ class SrvGrid( roAgentRunsRepository : RoReportsExecutionRepository - , asyncComplianceService : AsyncComplianceService + , asyncComplianceService: AsyncComplianceService + , configService : ReadConfigService ) extends Loggable { private def templatePath = List("templates-hidden", "srv_grid") @@ -111,7 +118,16 @@ class SrvGrid( , callback : Option[(String, Boolean) => JsCmd] = None , refreshNodes : Option[ () => Seq[NodeInfo]] = None ) : NodeSeq = { - tableXml( tableId) ++ Script(OnLoad(initJs(tableId,nodes,callback,refreshNodes))) + val script = { + configService.rudder_global_policy_mode() match { + case Full(globalPolicyMode) => Script(OnLoad(initJs(tableId,nodes,globalPolicyMode,callback,refreshNodes))) + case eb : EmptyBox => + val fail = eb ?~! "Could not find global policy Mode" + logger.error(fail.messageChain) + NodeSeq.Empty + } + } + tableXml(tableId) ++ script } /** @@ -123,16 +139,21 @@ class SrvGrid( def initJs( tableId : String , nodes : Seq[NodeInfo] + , globalPolicyMode : GlobalPolicyMode , callback : Option[(String, Boolean) => JsCmd] , refreshNodes : Option[ () => Seq[NodeInfo]] ) : JsCmd = { val data = getTableData(nodes,callback) + val globalOverride = globalPolicyMode.overridable match { + case Always => true + case Unoverridable => false + } + val objGlobalPolicyMode = JsObj(("override"->globalOverride), ("policyMode"->globalPolicyMode.mode.name)) val refresh = refreshNodes.map(refreshData(_,callback,tableId).toJsCmd).getOrElse("undefined") - JsRaw(s"""createNodeTable("${tableId}",${data.json.toJsCmd},"${S.contextPath}",${refresh});""") - + JsRaw(s"""createNodeTable("${tableId}",${data.json.toJsCmd},"${S.contextPath}",${refresh}, ${objGlobalPolicyMode});""") } def getTableData ( @@ -149,8 +170,9 @@ class SrvGrid( val lines = (for { lastReports <- runs + globalMode <- configService.rudder_global_policy_mode() } yield { - nodes.map(node => NodeLine(node,lastReports.get(node.id), callback)) + nodes.map(node => NodeLine(node,lastReports.get(node.id), callback, globalMode)) }) match { case eb: EmptyBox => val msg = "Error when trying to get nodes info" @@ -209,8 +231,20 @@ case class NodeLine ( node : NodeInfo , lastReport : Box[Option[AgentRun]] , callback : Option[(String, Boolean) => JsCmd] + , globalMode : GlobalPolicyMode ) extends JsTableLine { + val (policyMode,explanation) = + (globalMode.overridable,node.policyMode) match { + case (Always,Some(Enforce)) | (Always,Some(Verify)) => + (node.policyMode.getOrElse(globalMode.mode),"

This mode is an override applied to this node. You can change it in the node's settings.

") + case (Always,_) => + val expl = """

This mode is the globally defined default. You can change it in settings.

You can also override it on this node in the node's settings.

""" + (node.policyMode.getOrElse(globalMode.mode), expl) + case (Unoverridable,_) => + (globalMode.mode, "

This mode is the globally defined default. You can change it in Settings.

") + } + val optCallback = { callback.map(cb => ("callback", AnonFunc("displayCompliance", ajaxCall(JsVar("displayCompliance"), s => { @@ -246,6 +280,8 @@ case class NodeLine ( , ( "osName") -> S.?(s"os.name.${node.osDetails.os.name}") , ( "osVersion" -> node.osDetails.version.value) , ( "servicePack" -> node.osDetails.servicePack.getOrElse("N/A")) + , ( "agentPolicyMode" -> policyMode.toString) + , ( "explanation" -> explanation.toString) , ( "lastReport" -> lastReportValue ) ) } diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/snippet/HomePage.scala b/rudder-web/src/main/scala/com/normation/rudder/web/snippet/HomePage.scala index 30b6d66d8e..a992128b37 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/snippet/HomePage.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/snippet/HomePage.scala @@ -97,7 +97,7 @@ case class GreenChart (value : Int) extends ComplianceLevelPieChart{ case class BlueChart (value : Int) extends ComplianceLevelPieChart{ val label = "Good (> 75%)" - val color = "#B2DE5B" + val color = "#9bc832" } case class OrangeChart (value : Int) extends ComplianceLevelPieChart{ diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/snippet/administration/PropertiesManagement.scala b/rudder-web/src/main/scala/com/normation/rudder/web/snippet/administration/PropertiesManagement.scala index e839bdaa81..b793e2bab3 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/snippet/administration/PropertiesManagement.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/snippet/administration/PropertiesManagement.scala @@ -63,6 +63,7 @@ import com.normation.rudder.reports.SyslogProtocol import com.normation.rudder.reports.GlobalComplianceMode import com.normation.rudder.web.components.popup.ModificationValidationPopup.Enable import com.normation.rudder.domain.appconfig.FeatureSwitch._ +import com.normation.rudder.web.components.AgentPolicyModeEditForm /** * This class manage the displaying of user configured properties. @@ -87,6 +88,7 @@ class PropertiesManagement extends DispatchSnippet with Loggable { case "workflow" => workflowConfiguration case "denyBadClocks" => cfserverNetworkConfiguration case "cfagentSchedule" => (xml) => cfagentScheduleConfiguration + case "agentPolicyMode" => (xml) => agentPolicyModeConfiguration case "complianceMode" => (xml) => complianceModeConfiguration case "cfengineGlobalProps" => cfengineGlobalProps case "loggingConfiguration" => loggingConfiguration @@ -557,7 +559,9 @@ class PropertiesManagement extends DispatchSnippet with Loggable { , globalMode ) } - + val agentPolicyModeEditForm = { + new AgentPolicyModeEditForm() + } def getSchedule() : Box[AgentRunInterval] = { for { starthour <- configService.agent_run_start_hour @@ -589,6 +593,7 @@ class PropertiesManagement extends DispatchSnippet with Loggable { } def cfagentScheduleConfiguration = agentScheduleEditForm.cfagentScheduleConfiguration + def agentPolicyModeConfiguration = agentPolicyModeEditForm.cfagentPolicyModeConfiguration def complianceModeConfiguration = complianceModeEditForm.complianceModeConfiguration def cfengineGlobalProps = { xml : NodeSeq => diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/snippet/configuration/DirectiveManagement.scala b/rudder-web/src/main/scala/com/normation/rudder/web/snippet/configuration/DirectiveManagement.scala index 6afa04ae3b..8f29b74c6c 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/snippet/configuration/DirectiveManagement.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/snippet/configuration/DirectiveManagement.scala @@ -67,6 +67,7 @@ import com.normation.rudder.authorization.NoRights import org.joda.time.DateTime import net.liftweb.http.js.JE.JsArray import com.normation.rudder.web.model.JsInitContextLinkUtil +import com.normation.rudder.domain.policies.GlobalPolicyMode /** * Snippet for managing the System and Active Technique libraries. @@ -86,9 +87,10 @@ class DirectiveManagement extends DispatchSnippet with Loggable { val getRules = () => RudderConfig.roRuleRepository.getAll() val uuidGen = RudderConfig.stringUuidGenerator val treeUtilService = RudderConfig.jsTreeUtilService + private[this] val configService = RudderConfig.configService def dispatch = { - RudderConfig.configService.rudder_workflow_enabled match { + configService.rudder_workflow_enabled match { case Full(workflowEnabled) => { case "head" => { _ => head(workflowEnabled) } @@ -171,8 +173,8 @@ class DirectiveManagement extends DispatchSnippet with Loggable { def displayDirectiveLibrary(workflowEnabled: Boolean): NodeSeq = { (
{ - (directiveLibrary,rules) match { - case (Full(activeTechLib), Full(allRules)) => + (directiveLibrary,rules,configService.rudder_global_policy_mode()) match { + case (Full(activeTechLib), Full(allRules), Full(globalMode)) => val usedDirectives = allRules.flatMap { case r => r.directiveIds.map( id => (id -> r.id)) }.groupBy( _._1 ).mapValues( _.size).toSeq @@ -180,6 +182,7 @@ class DirectiveManagement extends DispatchSnippet with Loggable {
    { DisplayDirectiveTree.displayTree( activeTechLib + , globalMode , usedDirectives , None , Some(onClickActiveTechnique) @@ -187,9 +190,9 @@ class DirectiveManagement extends DispatchSnippet with Loggable { , false ) }
- case (x, y) => + case (x, y, z) => - (x :: y :: Nil).foreach { + (x :: y :: z :: Nil).foreach { case eb: EmptyBox => val f = eb ?~! "Error when trying to get the root category of Active Techniques" logger.error(f.messageChain) @@ -233,26 +236,34 @@ class DirectiveManagement extends DispatchSnippet with Loggable { case None => ".page-title *" #> "Usage" & "#details *" #> { -
-

Directives are displayed in the tree of - - Active Techniques - , - grouped by categories.

-
    -
  • Fold/unfold category folders;
  • -
  • Click on the name of a Technique to see its description;
  • -
  • - Click on the name of a Directive to see its configuration items. - Details of the Technique it's based on will also be displayed. -
  • -
-

Additional Techniques may be available through the - - Techniques screen - . -

-
+
+
+
+
+ +
+

+ Directives are displayed in the tree of + + Active Techniques + , grouped by categories. +

+
    +
  • Fold/unfold category folders;
  • +
  • Click on the name of a Technique to see its description;
  • +
  • + Click on the name of a Directive to see its configuration items. + Details of the Technique it's based on will also be displayed. +
  • +
+

Additional Techniques may be available through the + + Techniques screen + . +

+
+
+
} case Some((fullActiveTechnique,version)) => @@ -272,48 +283,49 @@ class DirectiveManagement extends DispatchSnippet with Loggable { } case Some(technique) => - /* - * We want to filter technique to only show the one - * with registered acceptation date time. - * Also sort by version, reverse - */ - val validTechniqueVersions = fullActiveTechnique.techniques.map { case(v, t) => - fullActiveTechnique.acceptationDatetimes.get(v) match { - case Some(timeStamp) => Some((v, t, timeStamp)) - case None => - logger.error("Inconsistent Technique version state for Technique with ID '%s' and its version '%s': ".format(fullActiveTechnique.techniqueName, v.toString) + - "that version was not correctly registered into Rudder and can not be use for now.") - logger.info("A workaround is to remove that version manually from Rudder (move the directory for that version of the Technique out " + - "of your configuration-repository directory (for example in /tmp) and 'git commit' the modification), " + - "reload the Technique Library, then add back the version back (move it back at its place, 'git add' the directory, 'git commit' the" + - "modification), and reload again the Technique Library.") - - None - } - }.toSeq.flatten.sortBy( _._1 ) - "#directiveIntro " #> { - currentDirectiveSettingForm.is.map { piForm => - (".directive *" #> piForm.directive.name) - } - } & - "#techniqueName" #> technique.name & - "#compatibility" #> technique.compatible.map { comp => - { if(comp.os.isEmpty) { - NodeSeq.Empty - } else { -

Supported operating systems: {comp.os.mkString(", ")}

- } } ++ { - if (comp.agents.isEmpty) { - NodeSeq.Empty - } else { -

Supported agents: {comp.agents.mkString(", ")}

- } } - } & - "#techniqueDescription" #> technique.description & - "#techniqueLongDescription" #> technique.longDescription & - "#isSingle *" #> showIsSingle(technique) & - "#techniqueVersion *+" #> showVersions(fullActiveTechnique, validTechniqueVersions, workflowEnabled) + /* + * We want to filter technique to only show the one + * with registered acceptation date time. + * Also sort by version, reverse + */ + val validTechniqueVersions = fullActiveTechnique.techniques.map { case(v, t) => + fullActiveTechnique.acceptationDatetimes.get(v) match { + case Some(timeStamp) => Some((v, t, timeStamp)) + case None => + logger.error("Inconsistent Technique version state for Technique with ID '%s' and its version '%s': ".format(fullActiveTechnique.techniqueName, v.toString) + + "that version was not correctly registered into Rudder and can not be use for now.") + logger.info("A workaround is to remove that version manually from Rudder (move the directory for that version of the Technique out " + + "of your configuration-repository directory (for example in /tmp) and 'git commit' the modification), " + + "reload the Technique Library, then add back the version back (move it back at its place, 'git add' the directory, 'git commit' the" + + "modification), and reload again the Technique Library.") + + None + } + }.toSeq.flatten.sortBy( _._1 ) + + "#directiveIntro " #> { + currentDirectiveSettingForm.is.map { piForm => + (".directive *" #> piForm.directive.name) + } + } & + "#techniqueName" #> technique.name & + "#compatibility" #> technique.compatible.map { comp => + { if(comp.os.isEmpty) { + NodeSeq.Empty + } else { +

Supported operating systems: {comp.os.mkString(", ")}

+ } } ++ { + if (comp.agents.isEmpty) { + NodeSeq.Empty + } else { +

Supported agents: {comp.agents.mkString(", ")}

+ } } + } & + "#techniqueDescription" #> technique.description & + "#techniqueLongDescription" #> technique.longDescription & + "#isSingle *" #> showIsSingle(technique) & + "#techniqueVersion *+" #> showVersions(fullActiveTechnique, validTechniqueVersions, workflowEnabled) } }) } @@ -391,25 +403,36 @@ class DirectiveManagement extends DispatchSnippet with Loggable { } private[this] def newDirective(technique: Technique, activeTechnique: FullActiveTechnique, workflowEnabled: Boolean) = { - val allDefaults = techniqueRepository.getTechniquesInfo.directivesDefaultNames - val directiveDefaultName = allDefaults.get(technique.id.toString).orElse(allDefaults.get(technique.id.name.value)).getOrElse(technique.name) - val directive = - Directive( - DirectiveId(uuidGen.newUuid) - , technique.id.version - , Map() - , directiveDefaultName - , "" - , None - , "" - , 5 - , true - ) - updateDirectiveSettingForm(activeTechnique, directive,None, workflowEnabled, true) - //Update UI - Replace(htmlId_policyConf, showDirectiveDetails) & - SetHtml(html_techniqueDetails, NodeSeq.Empty) & - JsRaw("""createTooltip();""") + configService.rudder_global_policy_mode() match { + case Full(globalMode) => + val allDefaults = techniqueRepository.getTechniquesInfo.directivesDefaultNames + val directiveDefaultName = allDefaults.get(technique.id.toString).orElse(allDefaults.get(technique.id.name.value)).getOrElse(technique.name) + val directive = + Directive( + DirectiveId(uuidGen.newUuid) + , technique.id.version + , Map() + , directiveDefaultName + , "" + , None + , "" + , 5 + , true + ) + updateDirectiveSettingForm(activeTechnique, directive,None, workflowEnabled, true, globalMode) + //Update UI + Replace(htmlId_policyConf, showDirectiveDetails) & + SetHtml(html_techniqueDetails, NodeSeq.Empty) & + JsRaw("""createTooltip();""") + case eb:EmptyBox => + val fail = eb ?~! "Could not get global policy mode while creating new Directive" + logger.error(fail.messageChain) + val errorHtml = +
+

{fail.messageChain}

+
+ SetHtml(htmlId_policyConf, errorHtml) + } } /* Update Directive form, @@ -424,15 +447,20 @@ class DirectiveManagement extends DispatchSnippet with Loggable { case Left(directive) => directive.id case Right(directiveId) => directiveId } - directiveLibrary.flatMap( _.allDirectives.get(directiveId)) match { - // The directive exists, update directive form - case Full((activeTechnique, directive)) => - // In priority, use the directive passed as parameter - val newDirective = directiveInfo match { - case Left(updatedDirective) => updatedDirective - case _ => directive // Only the id, get it from the library + configService.rudder_global_policy_mode() match { + case Full(globalMode) => + directiveLibrary.flatMap( _.allDirectives.get(directiveId)) match { + // The directive exists, update directive form + case Full((activeTechnique, directive)) => + // In priority, use the directive passed as parameter + val newDirective = directiveInfo match { + case Left(updatedDirective) => updatedDirective + case _ => directive // Only the id, get it from the library + } + updateDirectiveSettingForm(activeTechnique, newDirective, oldDirective, workflowEnabled, false, globalMode) + case eb:EmptyBox => + currentDirectiveSettingForm.set(eb) } - updateDirectiveSettingForm(activeTechnique, newDirective, oldDirective, workflowEnabled, false) case eb:EmptyBox => currentDirectiveSettingForm.set(eb) } @@ -449,6 +477,7 @@ class DirectiveManagement extends DispatchSnippet with Loggable { , oldDirective : Option[Directive] , workflowEnabled : Boolean , isADirectiveCreation: Boolean + , globalMode : GlobalPolicyMode ) : Unit = { activeTechnique.techniques.get(directive.techniqueVersion) match { @@ -461,6 +490,7 @@ class DirectiveManagement extends DispatchSnippet with Loggable { , directive , oldDirective , workflowEnabled + , globalMode , onSuccessCallback = directiveEditFormSuccessCallBack(workflowEnabled) , onMigrationCallback = (dir,optDir) => updateDirectiveForm(workflowEnabled)(Left(dir),optDir) & displayFinishMigrationPopup , isADirectiveCreation = isADirectiveCreation diff --git a/rudder-web/src/main/webapp/javascript/rudder/angular/auditMode.js b/rudder-web/src/main/webapp/javascript/rudder/angular/auditMode.js new file mode 100644 index 0000000000..3c13454539 --- /dev/null +++ b/rudder-web/src/main/webapp/javascript/rudder/angular/auditMode.js @@ -0,0 +1,164 @@ + +var app = angular.module('auditmode', []); + +app.factory('configGlobalFactory', function ($http){ + //Case : global configuration + this.policyMode = { + url : "/rudder/secure/api/latest/settings/global_policy_mode" + , getValue : function(){ + return $http.get(this.url).then(function successCallback(response) { + return response.data.data.settings.global_policy_mode; + }, function errorCallback(response) { + console.error('error - policy mode'); + }); + } + , save : function(mode){ + var data = {'value' : mode}; + return $http.post(this.url, data).then(function successCallback(response) { + return response.status==200; + }, function errorCallback(response) { + return response.status==200; + }); + } + }; + this.overrideMode = { + url : "/rudder/secure/api/latest/settings/global_policy_mode_overridable" + , getValue : function(){ + return $http.get(this.url).then(function successCallback(response) { + return response.data.data.settings.global_policy_mode_overridable; + }, function errorCallback(response) { + console.error('error - policy mode'); + }); + } + , save : function(mode){ + var data = {'value' : mode}; + return $http.post(this.url, data).then(function successCallback(response) { + return response.status==200; + }, function errorCallback(response) { + return response.status==200; + }); + } + }; + return this; +}); + +app.factory('configNodeFactory', function ($http){ + //Case : node configuration + this.policyMode = { + url : "/rudder/secure/api/latest/nodes/" + getNodeId() + , getValue : function(){ + return $http.get(this.url).then(function successCallback(response) { + return response.data.data.nodes[0].policyMode; + }, function errorCallback(response) { + console.error('error - policy mode'); + }); + } + , save : function(mode){ + var data = {'policyMode' : mode}; + return $http.post(this.url, data).then(function successCallback(response) { + return response.status==200; + }, function errorCallback(response) { + return response.status==200; + }); + } + }; + return this; +}); +app.controller('auditmodeCtrl', function ($scope, $http, $location, $timeout, configGlobalFactory, configNodeFactory) { + var nodeId = getNodeId(); + // variable used for saving animations + $scope.saving = { + 'policyMode' : 0 + , 'overrideMode' : 0 + }; + // global configuration + $scope.isGlobalForm; + $scope.globalConfiguration = {}; + // current configuration + $scope.currentConf = {}; + // modified configuration + $scope.conf = {}; + // appropriated factory + $scope.factory; + + // -- Get global configuration + configGlobalFactory.policyMode.getValue().then(function(currentPolicyMode){ + $scope.globalConfiguration.policyMode = currentPolicyMode; + if(!nodeId){ + $scope.conf.policyMode = currentPolicyMode; + } + }); + configGlobalFactory.overrideMode.getValue().then(function(currentOverrideMode){ + $scope.globalConfiguration.overrideMode = currentOverrideMode; + if(!nodeId){ + $scope.conf.overrideMode = $scope.currentConf.overrideMode; + } + }); + + // -- Get appropriated factory and initialize scope + if(nodeId){ + // case : node + $scope.isGlobalForm = false; + $scope.factory = configNodeFactory; + $scope.factory.policyMode.getValue().then(function(currentPolicyMode){ + $scope.currentConf.policyMode = currentPolicyMode; + $scope.conf.policyMode = currentPolicyMode; + }); + }else{ + // case : global + $scope.isGlobalForm = true; + $scope.factory = configGlobalFactory; + $scope.currentConf = $scope.globalConfiguration; + } + + // -- Detect current modifications + $scope.$watch('conf', function(){ + $scope.nochange = angular.equals($scope.currentConf,$scope.conf); + }, true); + + // -- Save modifications + $scope.saveChanges = function($event) { + if(!$scope.nochange){ + //Start loading animation + $scope.saving = { + 'policyMode' : 1 + , 'overrideMode' : 1 + }; + $scope.factory.policyMode.save($scope.conf.policyMode).then(function(success){ + if(success){ + //Reinitialize scope + $scope.errorFeedback = false; + $scope.nochange = true; + $scope.currentConf.policyMode = $scope.conf.policyMode; + $scope.saving.policyMode = 2; + }else{ + $scope.errorFeedback = true; + $scope.saving.policyMode = 2; + } + }); + if($scope.isGlobalForm){ + $scope.factory.overrideMode.save($scope.conf.overrideMode).then(function(success){ + if(success){ + //Reinitialize scope + $scope.errorFeedback = false; + $scope.nochange = true; + $scope.currentConf.overrideMode = $scope.conf.overrideMode; + $scope.saving.overrideMode = 2; + }else{ + $scope.errorFeedback = true; + $scope.saving.overrideMode = 2; + } + }); + } + } + }; +}); +function getNodeId(){ + var nodeId; + try { + var hash = JSON.parse(decodeURI(window.location.hash).slice(2)); + nodeId = hash.nodeId; + } catch(err){} + return nodeId; +} + diff --git a/rudder-web/src/main/webapp/javascript/rudder/homePage.js b/rudder-web/src/main/webapp/javascript/rudder/homePage.js index 8369f6477f..f228ac1f46 100644 --- a/rudder-web/src/main/webapp/javascript/rudder/homePage.js +++ b/rudder-web/src/main/webapp/javascript/rudder/homePage.js @@ -75,7 +75,7 @@ function homePage ( colorStart: '#6FADCF', // Colors colorStop: '#8FC0DA', // just experiment with them strokeColor: '#E0E0E0', // to see which ones work best for you - percentColors : [[0.0, "#d9534f" ], [0.30, "#f0ad4e"], [0.50, "#5bc0de"], [1.0, "#5cb85c"]], + percentColors : [[0.0, "#d9534f" ], [0.30, "#f0ad4e"], [0.50, "#5bc0de"], [1.0, "#9bc832"]], generateGradient: true }; var target = document.getElementById('complianceGauge'); // your canvas element @@ -110,7 +110,7 @@ function homePage ( } function displayInventoryGraph (id,data) { - var colorPatternDonutCharts = ['rgb(31, 119, 180)', 'rgb(23, 190, 207)', 'rgb(255, 127, 14)', 'rgb(255, 224, 14)', 'rgb(227, 119, 194)', 'rgb(44, 160, 44)', 'rgb(255, 104, 105)', 'rgb(148, 103, 189)', 'rgb(140, 86, 75)', 'rgb(160, 160, 160)', 'rgb(146, 201, 74)', '#ffd203', 'rgb(132, 63, 152)']; + var colorPatternDonutCharts = ['rgb(54, 148, 209)', 'rgb(23, 190, 207)', 'rgb(255,113,37)', 'rgb(255, 224, 14)', 'rgb(227, 119, 194)', 'rgb(44, 160, 44)', 'rgb(255, 104, 105)', 'rgb(148, 103, 189)', 'rgb(140, 86, 75)', 'rgb(160, 160, 160)', 'rgb(155,200,50)', '#ffd203', 'rgb(132, 63, 152)']; var smallHeight = $(window).height() / 4 ; diff --git a/rudder-web/src/main/webapp/javascript/rudder/rudder-datatable.js b/rudder-web/src/main/webapp/javascript/rudder/rudder-datatable.js index a2aeeb1af7..2c073426c6 100644 --- a/rudder-web/src/main/webapp/javascript/rudder/rudder-datatable.js +++ b/rudder-web/src/main/webapp/javascript/rudder/rudder-datatable.js @@ -178,7 +178,7 @@ function recentChangesText(id) { // Elem Content graphElem.text(count).addClass("center") graphElem.append(toolTipContainer); - createTooltip(); + createTooltip();$('.rudder-label').bsTooltip(); } /* @@ -201,7 +201,6 @@ function recentChangesText(id) { * } */ function createRuleTable(gridId, data, needCheckbox, needActions, needCompliance, addRecentChanges, allCheckboxCallback, contextPath, refresh, isPopup) { - //base element for the clickable cells function callbackElement(oData, action) { var elem = $(""); @@ -251,10 +250,9 @@ function createRuleTable(gridId, data, needCheckbox, needActions, needCompliance , "sTitle": "Name" , "fnCreatedCell" : function (nTd, sData, oData, iRow, iCol) { var data = oData; - - var action = "showForm" + var action = "showForm"; if (! needActions) { - action = "showEditForm" + action = "showEditForm"; } // Define the elem and its callback var elem = callbackElement(oData, action); @@ -286,6 +284,9 @@ function createRuleTable(gridId, data, needCheckbox, needActions, needCompliance // Append the content to the row $(nTd).empty(); $(nTd).prepend(elem); + $(nTd).addClass('tw-bs'); + var badge = createBadgeAgentPolicyMode('rule',data.policyMode, data.explanation); + $(nTd).prepend(badge); } }; @@ -433,7 +434,7 @@ function createRuleTable(gridId, data, needCheckbox, needActions, needCompliance if(isPopup){ $('#'+gridId).addClass("table table-hover table-striped table-bordered"); } - createTooltip(); + createTooltip();$('.rudder-label').bsTooltip(); // Add callback to checkbox column $("#checkAll").prop("checked", false); @@ -475,14 +476,14 @@ function createRuleTable(gridId, data, needCheckbox, needActions, needCompliance * } */ function createRuleComplianceTable(gridId, data, contextPath, refresh) { - var columns = [ { "sWidth": "75%" , "mDataProp": "rule" , "sTitle": "Rule" , "fnCreatedCell" : function (nTd, sData, oData, iRow, iCol) { - $(nTd).addClass("listopen"); - + $(nTd).addClass("listopen tw-bs"); + $(nTd).empty(); + $(nTd).text(oData.rule); if (! oData.isSystem) { var editLink = $(""); editLink.attr("href",contextPath + '/secure/configurationManager/ruleManagement#{"ruleId":"'+oData.id+'"}'); @@ -491,8 +492,8 @@ function createRuleComplianceTable(gridId, data, contextPath, refresh) { editLink.click(function(e) {e.stopPropagation();}); editLink.append(editIcon); editLink.addClass("reportIcon"); - $(nTd).append(editLink); + $(nTd).prepend(createBadgeAgentPolicyMode('rule', oData.policyMode, oData.explanation)); } } } , { @@ -521,6 +522,7 @@ function createRuleComplianceTable(gridId, data, contextPath, refresh) { , "aaSorting": [[ 0, "asc" ]] , "fnDrawCallback" : function( oSettings ) { createInnerTable(this, createDirectiveTable(false, true, contextPath), contextPath, "rule"); + $('.rudder-label').bsTooltip(); } , "sDom": '<"dataTables_wrapper_top newFilter"f<"dataTables_refresh">>rt<"dataTables_wrapper_bottom"lip>' }; @@ -554,7 +556,7 @@ function createExpectedReportTable(gridId, data, contextPath, refresh) { "mDataProp": "value" , "sTitle" : "Value" } ]; - return function (gridId,data) {createTable(gridId, data, columns, defaultParams, contextPath); createTooltip();} + return function (gridId,data) {createTable(gridId, data, columns, defaultParams, contextPath); createTooltip();$('.rudder-label').bsTooltip();} }; var localComponentTable = function() { @@ -569,7 +571,6 @@ function createExpectedReportTable(gridId, data, contextPath, refresh) { var params = jQuery.extend({"fnDrawCallback" : function( oSettings ) { createInnerTable(this, localNodeComponentValueTable()); }}, defaultParams); - return function (gridId,data) {createTable(gridId,data,columns, params, contextPath);} }; @@ -578,8 +579,7 @@ function createExpectedReportTable(gridId, data, contextPath, refresh) { "mDataProp": "directive" , "sTitle": "Directive" , "fnCreatedCell" : function (nTd, sData, oData, iRow, iCol) { - $(nTd).addClass("listopen"); - + $(nTd).addClass("listopen tw-bs"); var tooltipIcon = $(""); tooltipIcon.attr("src",contextPath + "/images/ic_question_14px.png"); tooltipIcon.addClass("reportIcon"); @@ -593,7 +593,6 @@ function createExpectedReportTable(gridId, data, contextPath, refresh) { $(nTd).append(tooltipIcon); $(nTd).append(toolTipContainer); - if (! oData.isSystem) { var editLink = $(""); editLink.attr("href",contextPath + '/secure/configurationManager/directiveManagement#{"directiveId":"'+oData.id+'"}'); @@ -602,7 +601,8 @@ function createExpectedReportTable(gridId, data, contextPath, refresh) { editLink.click(function(e) {e.stopPropagation();}); editLink.append(editIcon); editLink.addClass("reportIcon"); - + var policyMode = oData.policyMode ? oData.policyMode : ""; + $(nTd).prepend(createBadgeAgentPolicyMode('directive', policyMode, oData.explanation)); $(nTd).append(editLink); } } @@ -615,7 +615,7 @@ function createExpectedReportTable(gridId, data, contextPath, refresh) { return function (gridId, data, refresh) { createTable(gridId, data, columns, params, contextPath, refresh); - createTooltip(); + createTooltip();$('.rudder-label').bsTooltip(); } }; @@ -623,8 +623,8 @@ function createExpectedReportTable(gridId, data, contextPath, refresh) { "mDataProp": "rule" , "sTitle" : "Rule" , "fnCreatedCell" : function (nTd, sData, oData, iRow, iCol) { - $(nTd).addClass("listopen"); - + $(nTd).addClass("listopen tw-bs"); + $(nTd).text(oData.rule); if (! oData.isSystem) { var editLink = $(""); editLink.attr("href",contextPath + '/secure/configurationManager/ruleManagement#{"ruleId":"'+oData.id+'"}'); @@ -633,13 +633,14 @@ function createExpectedReportTable(gridId, data, contextPath, refresh) { editLink.click(function(e) {e.stopPropagation();}); editLink.append(editIcon); editLink.addClass("reportIcon"); - $(nTd).append(editLink); + $(nTd).prepend(createBadgeAgentPolicyMode('rule', oData.policyMode, oData.explanation)); } } } ]; var params = jQuery.extend({"fnDrawCallback" : function( oSettings ) { createInnerTable(this, localDirectiveTable(), contextPath, "rule"); + $('.rudder-label').bsTooltip(); } , "sDom": '<"dataTables_wrapper_top newFilter"f<"dataTables_refresh">>rt<"dataTables_wrapper_bottom"lip>' } @@ -668,7 +669,6 @@ function createExpectedReportTable(gridId, data, contextPath, refresh) { * } */ function createDirectiveTable(isTopLevel, isNodeView, contextPath) { - if (isTopLevel) { var complianceWidth = "25%"; var directiveWidth = "75%"; @@ -682,8 +682,9 @@ function createDirectiveTable(isTopLevel, isNodeView, contextPath) { , "mDataProp": "directive" , "sTitle": "Directive" , "fnCreatedCell" : function (nTd, sData, oData, iRow, iCol) { - $(nTd).addClass("listopen"); - + $(nTd).empty(); + $(nTd).append(document.createTextNode(oData.directive)); + $(nTd).addClass("listopen tw-bs"); var tooltipIcon = $(""); tooltipIcon.attr("src",contextPath + "/images/ic_question_14px.png"); tooltipIcon.addClass("reportIcon"); @@ -694,10 +695,8 @@ function createDirectiveTable(isTopLevel, isNodeView, contextPath) { var toolTipContainer= $("
Directive '"+sData+"' is based on technique '"+oData.techniqueName+"' (version "+oData.techniqueVersion+")
"); toolTipContainer.addClass("tooltipContent"); toolTipContainer.attr("id",tooltipId); - $(nTd).append(tooltipIcon); $(nTd).append(toolTipContainer); - if (! oData.isSystem) { var editLink = $("
"); editLink.attr("href",contextPath + '/secure/configurationManager/directiveManagement#{"directiveId":"'+oData.id+'"}'); @@ -706,8 +705,9 @@ function createDirectiveTable(isTopLevel, isNodeView, contextPath) { editLink.click(function(e) {e.stopPropagation();}); editLink.append(editIcon); editLink.addClass("reportIcon"); - $(nTd).append(editLink); + var policyMode = oData.policyMode ? oData.policyMode : policyMode ; + $(nTd).prepend(createBadgeAgentPolicyMode('directive', policyMode, oData.explanation)); } } } , { @@ -730,6 +730,7 @@ function createDirectiveTable(isTopLevel, isNodeView, contextPath) { , "aaSorting": [[ 0, "asc" ]] , "fnDrawCallback" : function( oSettings ) { createInnerTable(this, createComponentTable(isTopLevel, isNodeView, contextPath), contextPath, "directive"); + $('.rudder-label').bsTooltip(); } }; @@ -745,7 +746,7 @@ function createDirectiveTable(isTopLevel, isNodeView, contextPath) { return function (gridId, data, refresh) { createTable(gridId, data, columns, params, contextPath, refresh); - createTooltip(); + createTooltip();$('.rudder-label').bsTooltip(); } } @@ -769,8 +770,7 @@ function createNodeComplianceTable(gridId, data, contextPath, refresh) { , "mDataProp": "node" , "sTitle": "Node" , "fnCreatedCell" : function (nTd, sData, oData, iRow, iCol) { - $(nTd).addClass("listopen"); - + $(nTd).addClass("listopen tw-bs"); var editLink = $(""); editLink.attr("href",contextPath +'/secure/nodeManager/searchNodes#{"nodeId":"'+oData.id+'"}'); var editIcon = $(""); @@ -778,8 +778,8 @@ function createNodeComplianceTable(gridId, data, contextPath, refresh) { editLink.click(function(e) {e.stopPropagation();}); editLink.append(editIcon); editLink.addClass("reportIcon"); - $(nTd).append(editLink); + $(nTd).prepend(createBadgeAgentPolicyMode('node', oData.policyMode, oData.explanation)); } } , { "sWidth": "25%" @@ -807,13 +807,14 @@ function createNodeComplianceTable(gridId, data, contextPath, refresh) { , "aaSorting": [[ 0, "asc" ]] , "fnDrawCallback" : function( oSettings ) { createInnerTable(this,createDirectiveTable(false, true, contextPath),"node"); + $('.rudder-label').bsTooltip(); } , "sDom": '<"dataTables_wrapper_top newFilter"f<"dataTables_refresh">>rt<"dataTables_wrapper_bottom"lip>' }; createTable(gridId, data, columns, params, contextPath, refresh); - createTooltip(); + createTooltip();$('.rudder-label').bsTooltip(); } /* @@ -927,7 +928,7 @@ function createNodeComponentValueTable(contextPath) { , "aaSorting": [[ 0, "asc" ]] } - return function (gridId,data) {createTable(gridId, data, columns, params, contextPath); createTooltip();} + return function (gridId,data) {createTable(gridId, data, columns, params, contextPath); createTooltip();$('.rudder-label').bsTooltip();} } @@ -972,7 +973,7 @@ function createRuleComponentValueTable (contextPath) { , "aaSorting": [[ 0, "asc" ]] } - return function (gridId,data) {createTable(gridId, data, columns, params, contextPath); createTooltip();} + return function (gridId,data) {createTable(gridId, data, columns, params, contextPath); createTooltip();$('.rudder-label').bsTooltip();} } @@ -997,7 +998,6 @@ function createRuleComponentValueTable (contextPath) { * } */ function createNodeTable(gridId, data, contextPath, refresh) { - //base element for the clickable cells function callbackElement(oData, displayCompliance) { var elem = $(""); @@ -1042,6 +1042,15 @@ function createNodeTable(gridId, data, contextPath, refresh) { "sWidth": "5%" , "mDataProp": "servicePack" , "sTitle": "OS SP" + } , { + "mDataProp": "agentPolicyMode" + , "sWidth": "8%" + , "sTitle": "Policy Mode" + , "fnCreatedCell" : function (nTd, sData, oData, iRow, iCol) { + $(nTd).empty(); + $(nTd).addClass('tw-bs'); + $(nTd).prepend(createTextAgentPolicyMode(true,oData.agentPolicyMode,oData.explanation)); + } } , { "mDataProp": "name" , "sWidth": "25%" @@ -1056,7 +1065,7 @@ function createNodeTable(gridId, data, contextPath, refresh) { $(nTd).prepend(link); } } , { - "sWidth": "20%" + "sWidth": "12%" , "mDataProp": "lastReport" , "sTitle": "Last seen" } ]; @@ -1070,6 +1079,7 @@ function createNodeTable(gridId, data, contextPath, refresh) { "sSearch": "" } , "fnDrawCallback": function( oSettings ) { + $('[data-toggle="tooltip"]').bsTooltip(); var rows = this._('tr', {"page":"current"}); $.each(rows, function(index,row) { // Display compliance progress bar if it has already been computed @@ -1078,13 +1088,13 @@ function createNodeTable(gridId, data, contextPath, refresh) { $("#compliance-bar-"+row.id).html(buildComplianceBar(compliance)); } }) + $('.rudder-label').bsTooltip(); } , "aaSorting": [[ 0, "asc" ]] , "sDom": '<"dataTables_wrapper_top newFilter"f<"dataTables_refresh">>rt<"dataTables_wrapper_bottom"lip>' }; createTable(gridId,data, columns, params, contextPath, refresh, "nodes"); - } /* @@ -1209,13 +1219,13 @@ function createTechnicalLogsTable(gridId, data, contextPath, refresh, pickEventL * { "executionDate" : Date report was executed [DateTime] * , "node": node hostname [String] * , "directiveName": Directive name [String] + * , "directiveId": Directive id [String] * , "component" : Report component [String] * , "value" : Report value [String] * , "message" : Report message [String] * } */ function createChangesTable(gridId, data, contextPath, refresh) { - var columns = [ { "sWidth": "8%" , "mDataProp": "executionDate" @@ -1521,6 +1531,7 @@ function changeCursor(clickable){ * Function to define opening of an inner table */ function createInnerTable(myTable, createFunction, contextPath, kind) { + var plusTd = $(myTable.fnGetNodes()); plusTd.each( function () { $(this).unbind(); diff --git a/rudder-web/src/main/webapp/javascript/rudder/rudder.js b/rudder-web/src/main/webapp/javascript/rudder/rudder.js index 4d13ed1465..991ebc347f 100644 --- a/rudder-web/src/main/webapp/javascript/rudder/rudder.js +++ b/rudder-web/src/main/webapp/javascript/rudder/rudder.js @@ -49,30 +49,13 @@ var bootstrapTab = $.fn.tab.noConflict(); var bootstrapAffix = $.fn.affix.noConflict(); var bootstrapModal = $.fn.modal.noConflict(); $.fn.bsModal = bootstrapModal; - - +$.fn.bsTooltip = bootstrapTooltip; /** * Instanciate the tooltip * For each element having the "tooltipable" class, when hovering it will look for it's * tooltipid attribute, and display in the tooltip the content of the div with the id * tooltipid */ -function createTooltiptr() { - $(".tooltipabletr").tooltip({ - show: { - effect: "none", - delay: 100 - }, - hide: { - effect: "none", - delay: 0 - }, - content: function () { - return $("#" + $(this).attr("tooltipid")).html(); - }, - track : true - }); -} function createTooltip() { $(".tooltipable").tooltip({ @@ -95,7 +78,6 @@ function createTooltip() { }); createTooltiptr(); } - function createTooltiptr() { $(".tooltipabletr").tooltip({ show: { @@ -606,4 +588,69 @@ $(document).ready(function() { createTooltip(); }); +function policyModeTooltip(kind, policyName, explanation){ + var tooltip = "" + + "

Agent Policy Mode

" + + "
"+ + "

This "+ kind +" is in "+ policyName +" mode.

"+ + "

"+ explanation +"

"+ + "
"; + return tooltip; +} + +function createTextAgentPolicyMode(isNode, currentPolicyMode, explanation){ + var policyMode = currentPolicyMode.toLowerCase(); + var nodeOrDirective = isNode ? "node" : "directive"; + var labelType = "label-"+policyMode; + var policyName = policyMode == "verify" ? "audit" : policyMode + var span = "" + var badge = $(span).get(0); + var tooltip = policyModeTooltip(nodeOrDirective, policyName, explanation); + badge.setAttribute("title", tooltip); + return badge; +} + +function createBadgeAgentPolicyMode(elmnt, currentPolicyMode, explanation){ + var policyMode = currentPolicyMode.toLowerCase(); + var labelType = "label-"+policyMode; + var policyName = policyMode == "verify" ? "audit" : policyMode; + var span = ""; + var badge = $(span).get(0); + var tooltip = policyModeTooltip(elmnt, policyName, explanation); + badge.setAttribute("title", tooltip); + return badge; +} +function getBadgePolicyMode(data){ + var enforce = audit = false; + for (rule in data){ + if((data[rule].policyMode=="verify")&&(!audit)){ + audit = true; + }else if((data[rule].policyMode=="enforce")&&(!enforce)){ + enforce = true; + } + } + if(enforce && audit){ + return "mixed"; + }else if(enforce){ + return "enforce"; + }else if(audit){ + return "audit"; + } + return "error"; +} +function createBadgeAgentPolicyModeMixed(data){ + var rules = data.details; + var badgePolicyMode = getBadgePolicyMode(rules); + var labelType = "label-"; + if(badgePolicyMode == "audit"){ + labelType += "verify"; + }else{ + labelType += badgePolicyMode; + } + var span = "" + var badge = $(span).get(0); + var tooltip = policyModeTooltip('policy', badgePolicyMode, ''); + badge.setAttribute("title", tooltip); + return badge; +} \ No newline at end of file diff --git a/rudder-web/src/main/webapp/javascript/rudder/tree.js b/rudder-web/src/main/webapp/javascript/rudder/tree.js index 91755f7fea..1d299c3412 100644 --- a/rudder-web/src/main/webapp/javascript/rudder/tree.js +++ b/rudder-web/src/main/webapp/javascript/rudder/tree.js @@ -201,6 +201,7 @@ var buildRuleCategoryTreeNoDnD = function(id, initially_select , appContext) { * Group tree */ var buildGroupTree = function(id, appContext, initially_select, select_multiple_modifier, select_node, authorized) { + if(select_multiple_modifier !== undefined) { select_limit = -1; } else { @@ -225,6 +226,7 @@ var buildGroupTree = function(id, appContext, initially_select, select_multiple_ $(id).bind("loaded.jstree", function (event, data) { data.instance.open_all(); + $(id+' .rudder-label').bsTooltip(); }).jstree({ "core" : { "animation" : 300, @@ -369,7 +371,8 @@ var buildDirectiveTree = function(id, initially_select, appContext, select_limit } $(id).bind("loaded.jstree", function (event, data) { data.instance.open_all(); - }).jstree({ + $(id+' .rudder-label').bsTooltip(); + }).jstree({ "core" : { "animation" : 300, "html_titles" : true @@ -422,7 +425,7 @@ var buildDirectiveTree = function(id, initially_select, appContext, select_limit "url" : appContext+"/javascript/jstree/themes/rudder/style.css" }, "plugins" : [ "themes", "html_data", "ui", "types", "search"] - }) + }); } diff --git a/rudder-web/src/main/webapp/secure/administration/policyServerManagement.html b/rudder-web/src/main/webapp/secure/administration/policyServerManagement.html index a853e3c3cc..f029e372a8 100644 --- a/rudder-web/src/main/webapp/secure/administration/policyServerManagement.html +++ b/rudder-web/src/main/webapp/secure/administration/policyServerManagement.html @@ -32,27 +32,27 @@ } - - - +
Client-server communication
- -
-
-

Configure the networks from which nodes are allowed - to connect to the Rudder policy servers to get their updated - configuration policy.

-

- You can add as many networks as you want, the expected - format is: NetworkIP/mask, for example - "42.42.0.0/16". -

-
+
+
+
+ +
+

Configure the networks from which nodes are allowed + to connect to the Rudder policy servers to get their updated + configuration policy.

+

+ You can add as many networks as you want, the expected + format is: NetworkIP/mask, for example + "42.42.0.0/16". +

+

@@ -156,6 +156,7 @@

Protocol

+
@@ -165,12 +166,18 @@

Protocol

-
-
-

If enabled, prompt users to enter a message explaining the reason for each configuration change they make.
- These messages will be stored in each Event log and as the commit message for the underlying git repository in

-
-
+
+
+
+ +
+

+ If enabled, prompt users to enter a message explaining the reason for each configuration change they make.
+ These messages will be stored in each Event log and as the commit message for the underlying git repository in + +

+
+

@@ -207,14 +214,21 @@

Protocol

Change Requests (validation workflow)
- -
-
-

If enabled, all changes to configuration (Directives, Rules, Groups and Parameters) will be submitted for validation via a Change Request.
-A new Change Request will enter the "Pending validation" status, then can be moved to "Pending deployment" (approved but not yet deployed) or "Deployed" (approved and deployed) statuses.
-Only users with the "validator" or "deployer" roles are authorized to perform these steps (see /opt/rudder/etc/rudder-users.xml).
-
-If disabled, all changes to configuration will be immediately deployed.

+
+
+
+ +
+

+ If enabled, all changes to configuration (Directives, Rules, Groups and Parameters) will be submitted for validation via a Change Request.
+ A new Change Request will enter the "Pending validation" status, then can be moved to "Pending deployment" (approved but not yet deployed) or "Deployed" (approved and deployed) statuses. +

+

+ Only users with the "validator" or "deployer" roles are authorized to perform these steps (see /opt/rudder/etc/rudder-users.xml). +

+

+ If disabled, all changes to configuration will be immediately deployed. +

@@ -251,11 +265,16 @@

Protocol

File retention
-
-
-

Every time Rudder modifies a file (by file editing or copying from a remote source), a copy of the overwritten file is kept under /var/rudder/modified-files/.
-Also, the full output from each agent run is stored in a file under /var/rudder/cfengine-community/outputs/. -These files are automatically removed to save on disk space. You can configure the retention time (Time To Live) they are kept for here.

+
+
+
+ +
+

Every time Rudder modifies a file (by file editing or copying from a remote source), a copy of the overwritten file is kept under /var/rudder/modified-files/.

+

+ Also, the full output from each agent run is stored in a file under /var/rudder/cfengine-community/outputs/. + These files are automatically removed to save on disk space. You can configure the retention time (Time To Live) they are kept for here. +

@@ -292,8 +311,11 @@

Protocol

-
-
+
+
+
+ +

All nodes in Rudder send reports via syslog to this Rudder root server. These logs are stored in an SQL database in order to determine compliance information displayed in this web interface. However, it can be useful to also store this information in a plain text log file, for example for statistics or debugging purposes. The option below enables this.

@@ -324,16 +346,24 @@

Protocol

-
-
- To help the Rudder team continue to improve this software day after day, we are running a survey to collect usage statistics.
- These statistics are submitted anonymously, and include overall statistics about your instance of Rudder +
+
+
+ +
+

To help the Rudder team continue to improve this software day after day, we are running a survey to collect usage statistics.

+

These statistics are submitted anonymously, and include overall statistics about your instance of Rudder (number of Rules, Directives, Nodes, etc). No potentially-sensitive data is included (only stock Rudder-provided techniques are examined, no hostnames, etc). - We highly value your privacy, as we do our own, so we will never share individual submissions (only globally compiled statistics).
- If you want to check the information that is sent, just run "/opt/rudder/bin/rudder-metrics-reporting -v" on your Rudder server. This won't send any information without your consent.
+ We highly value your privacy, as we do our own, so we will never share individual submissions (only globally compiled statistics) +

+

+ If you want to check the information that is sent, just run "/opt/rudder/bin/rudder-metrics-reporting -v" on your Rudder server. This won't send any information without your consent. +

+

This information is very valuable to the development team, as it helps us focus on the features that matter most and better understand what our users care about. Please consider participating in the survey! +

@@ -359,11 +389,14 @@

Protocol

-
-
- In Rules table, we display a graph for each Rule showing it's activity (number of repaired reports).
- Unfortunately, some browsers (especially Firefox) have trouble displaying them and make Rule pages almost unusable.
- If you experience slow loading of Rules pages, you can disable this feature here. +
+
+
+ +
+

In Rules table, we display a graph for each Rule showing it's activity (number of repaired reports).

+

Unfortunately, some browsers (especially Firefox) have trouble displaying them and make Rule pages almost unusable.

+

If you experience slow loading of Rules pages, you can disable this feature here.

@@ -389,8 +422,11 @@

Protocol

-
-
+
+
+
+ +

API response used to send data that may not be sent back to the API for modification. Parameter to enable a directive is 'enabled', but API response was using 'isEnabled' for that parameter, so you had to handle this case before sending data. We have harmonized response and parameters (in this ). @@ -424,11 +460,14 @@

Protocol

-
-
- If enabled, all fields can contain a JavaScript expression. - These expressions are evaluated during promise generation, and can therefore provide unique values for each node. - Read the script documentation for more information. +
+
+
+ +
+ If enabled, all password fields can contain a JavaScript expression. + These expressions are evaluated during promise generation, and can therefore provide unique values for each node. + Read the script documentation for more information.
@@ -453,8 +492,11 @@

Protocol

Clear policy caches
-
-
+
+
+
+ +

Clear cached data. This will trigger a full policy update, and regenerate all promise files.

@@ -483,12 +525,14 @@

Protocol

Manage dynamic groups
-
-
-

Groups in Rudder can be static (fixed list of nodes) or dynamic (the list of nodes is built from a search query).
-To take into account new nodes and changes to their inventory, dynamic groups must be reloaded regularly.
-Currently, Rudder will automatically do this reload every 5 minutes (see /opt/rudder/etc/rudder-web.properties). -

+
+
+
+ +
+

Groups in Rudder can be static (fixed list of nodes) or dynamic (the list of nodes is built from a search query).

+

To take into account new nodes and changes to their inventory, dynamic groups must be reloaded regularly.

+

Currently, Rudder will automatically do this reload every 5 minutes (see /opt/rudder/etc/rudder-web.properties).

@@ -512,12 +556,14 @@

Protocol

Manage Technique library
-
-
-

Techniques in Rudder are read from the filesystem (in /var/rudder/configuration-repository/techniques).
-To take into account new Techniques and changes, the Technique library must be updated regularly.
-Currently, Rudder will automatically do this update every 5 minutes (see /opt/rudder/etc/rudder-web.properties). -

+
+
+
+ +
+

Techniques in Rudder are read from the filesystem (in /var/rudder/configuration-repository/techniques).

+

To take into account new Techniques and changes, the Technique library must be updated regularly.

+

Currently, Rudder will automatically do this update every 5 minutes (see /opt/rudder/etc/rudder-web.properties).

diff --git a/rudder-web/src/main/webapp/secure/nodeManager/searchNodes.html b/rudder-web/src/main/webapp/secure/nodeManager/searchNodes.html index efc9964c31..672869af13 100644 --- a/rudder-web/src/main/webapp/secure/nodeManager/searchNodes.html +++ b/rudder-web/src/main/webapp/secure/nodeManager/searchNodes.html @@ -2,6 +2,7 @@ Rudder - Search Nodes +
@@ -57,5 +58,4 @@
- diff --git a/rudder-web/src/main/webapp/style/bootstrap/tw-bs-bootstrap-3.3.7.css b/rudder-web/src/main/webapp/style/bootstrap/tw-bs-bootstrap-3.3.7.css index ed2d83d768..72fd2e69d4 100644 --- a/rudder-web/src/main/webapp/style/bootstrap/tw-bs-bootstrap-3.3.7.css +++ b/rudder-web/src/main/webapp/style/bootstrap/tw-bs-bootstrap-3.3.7.css @@ -5253,9 +5253,7 @@ -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } -.tw-bs .progress-bar-success { - background-color: #5cb85c; -} + .tw-bs .progress-striped .progress-bar-success { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); diff --git a/rudder-web/src/main/webapp/style/rudder/rudder-menu.css b/rudder-web/src/main/webapp/style/rudder/rudder-menu.css index 8810cf2668..e950311dfb 100644 --- a/rudder-web/src/main/webapp/style/rudder/rudder-menu.css +++ b/rudder-web/src/main/webapp/style/rudder/rudder-menu.css @@ -34,29 +34,31 @@ * ************************************************************************************* */ - +.tw-bs h1,.tw-bs h2,.tw-bs h3,.tw-bs h4,.tw-bs h5,.tw-bs h6,.tw-bs .page-title,.tw-bs .rudder-form{ + font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} .text-align{ - text-align:center!important; + text-align:center!important; } /* - FOCUS INPUT BOOTSTRAP */ -.tw-bs .form-control:focus { - border-color: #222D32; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 7px rgba(180, 180, 180, 0.6); +.tw-bs .ruuder-form .form-control:focus { + border-color: #222D32; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 7px rgba(180, 180, 180, 0.6); } /* - - */ .tw-bs a, .tw-bs a:focus, .tw-bs a:hover, .tw-bs a:active{ - outline:none !important; + outline:none !important; } /* SPINNERS */ .tw-bs .btn i.fa-spin { position: absolute; - left: 14px; + left: 26px; font-size: 20px; } @@ -98,7 +100,7 @@ div.topQuickSearchResults.ac_results li { .skin-yellow .sidebar-form.topbar-search-form{ display: inline-block; margin: 0; - margin-left: 45px !important; + margin-left: 50px !important; position: relative; top: 6.5px; width: auto; @@ -175,51 +177,52 @@ header.main-header{ width: 100%; } aside.main-sidebar.fixed{ - position:fixed; + position:fixed; + z-index:850; } .no-hpadding{ - padding-left:0 !important; - padding-right:0 !important; + padding-left:0 !important; + padding-right:0 !important; } .hpadding{ - padding: 5px 15px; + padding: 5px 15px; } .nodeGroupContainer .col-sm{ - background-color:transparent; + background-color:transparent; } .nodeGroupContainer .col-sm>div{ - background-color:#fff; - float:left; - border-color: #ddd; - box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.15); - margin-bottom:30px; - padding:0 15px; - width: 100%; - height:auto; - min-height: calc(100vh - 110px); + background-color:#fff; + float:left; + border-color: #ddd; + box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.15); + margin-bottom:30px; + padding:0 15px; + width: 100%; + height:auto; + min-height: calc(100vh - 110px); } .tw-bs .content-scrollable{ - height: calc(100vh - 110px); - overflow-x: hidden; - overflow-y: scroll; + height: calc(100vh - 110px); + overflow-x: hidden; + overflow-y: scroll; } #groupDetails{ - height: calc(100vh - 110px); - overflow-x: hidden; - overflow-y: auto; + height: calc(100vh - 110px); + overflow-x: hidden; + overflow-y: auto; } a.sidebar-toggle{ - font-size:14px; + font-size:14px; } .main-header .navbar .nav>li.user.user-menu>a:hover,.main-header .navbar .nav>li.user.user-menu>a:focus,.main-header .navbar .nav>li.user.user-menu>a:visited,.main-header .navbar .nav>li.user.user-menu{ - background: inherit; - color: #fff; - cursor: default; + background: inherit; + color: #fff; + cursor: default; } .rudder_col { - background-color: rgb(236, 240, 245); - padding: 15px 15px 10px; + background-color: rgb(236, 240, 245); + padding: 15px 15px 10px; } .content-wrapper { height: auto; @@ -231,24 +234,24 @@ a.sidebar-toggle{ } } .tw-bs .wrapper{ - font-size:14px; - overflow: visible; + font-size:14px; + overflow: visible; } .main-header .navbar .sidebar-toggle { - line-height: 20px; - position:absolute; + line-height: 20px; + position:absolute; } .tw-bs aside form button { - width: auto; + width: auto; } .tw-bs aside form .form-control { - font-size:14px; + font-size:14px; } .tw-bs .navbar-nav > li > a { - padding: 15px 15px; + padding: 15px 15px; } .tw-bs .navbar-nav a .glyphicon,.tw-bs .navbar-nav a:hover .glyphicon,.tw-bs .navbar-nav a:focus .glyphicon { - color: #fff; + color: #fff; } .tw-bs .navbar-nav a .fa-sign-out{ font-size: 18px; @@ -256,90 +259,93 @@ a.sidebar-toggle{ position: relative; } #showhidetechnicalerrors{ - color:#000; + color:#000; } .tw-bs .wrapper a:focus,.tw-bs .wrapper a:hover { - text-decoration: none; + text-decoration: none; } .box-shadow{ - background-color: #fff; - border-color: #ddd; - box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.15); + background-color: #fff; + border-color: #ddd; + box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.15); } .inner-portlet { - margin:inherit; - background-color: #fff; + margin:inherit; + background-color: #fff; } .content-wrapper .tw-bs .panel-default { - border-color: #ddd; - box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.15); + border-color: #ddd; + box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.15); } .skin-yellow.sidebar-collapse .sidebar-menu>li>.treeview-menu { - margin-top: -2px; + margin-top: -2px; } .sidebar-menu>li>a { - line-height: 20px; + line-height: 20px; } .sidebar-menu>li>a>span>span>.fa, .sidebar-menu>li>a>span>span>.glyphicon, .sidebar-menu>li>a>span>span>.ion { - width: 20px; + width: 20px; } .rudder_col>.ui-widget-content,.rudder_col>.tw-bs>.ui-widget-content { - background-color:#fff; - box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.15); - border:1px solid #ddd; - padding: 0 15px 20px 15px; + background-color:#fff; + box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.15); + border:1px solid #ddd; + padding: 0 15px 20px 15px; } .ui-widget-content,.portlet-content{ - border-radius:4px; + border-radius:4px; } - .page-title{ border-bottom: 1px solid #f08004; margin: 15px; font-size: 18px; } +.inner-portlet #verifyMode h3 { + width: 100%; +} + .inner-portlet h3 { - padding: 5px 15px; - display: block; - width: calc(100% - 30px); - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; - margin-left: 15px; - font-size: 18px; + padding: 5px 15px; + display: block; + width: calc(100% - 30px); + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; + margin-left: 15px; + font-size: 18px; } /* ----- ----- */ #globalComplianceStats { - color: rgba(34, 45, 50, 0.75); - line-height: 20px; - height: auto; - padding-top: 15px; - border-top: 2px solid #EBE7E7; - margin-left: 20%; - width: 60%; - position:relative + color: rgba(34, 45, 50, 0.75); + line-height: 20px; + height: auto; + padding-top: 15px; + border-top: 2px solid #EBE7E7; + margin-left: 20%; + width: 60%; + position:relative } #globalComplianceStats:before { - content: ""; - width: 0; - top: -14px; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - border-bottom: 10px solid #EBE7E7; - display: block; - height: 13px; - position: absolute; - left: 50%; - margin-left: -10px; - z-index: 99; + content: ""; + width: 0; + top: -14px; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-bottom: 10px solid #EBE7E7; + display: block; + height: 13px; + position: absolute; + left: 50%; + margin-left: -10px; + z-index: 99; } .highlight{ - color: #222D32; - font-weight:700; + color: #222D32; + font-weight:700; } #globalCompliance .progress{ background-color: transparent; @@ -348,35 +354,35 @@ a.sidebar-toggle{ box-shadow: none; } .tw-bs .progress { - margin: 3px 5px; + margin: 3px 5px; } .tw-bs td, .tw-bs th { - padding: 4px 3px; + padding: 4px 3px; } .tw-bs .panel-default > .panel-heading { - color: #333; - background-color:#fff; - border-color: #F0F0F0; - margin: 0 10px 10px 10px; + color: #333; + background-color:#fff; + border-color: #F0F0F0; + margin: 0 10px 10px 10px; } .stats li>a .badge{ - background-color: #222D32; - opacity:.5; + background-color: #222D32; + opacity:.5; } .stats li>a:hover .badge{ - background-color: #222D32; - opacity:1; + background-color: #222D32; + opacity:1; } .tw-bs .nav-pills > li > a { - font-weight:300; - border-radius: 0px; - color: #222D32; - font-size: 15px; + font-weight:300; + border-radius: 0px; + color: #222D32; + font-size: 15px; } .tw-bs .nav > li > a:hover, .tw-bs .nav > li > a:focus, .tw-bs .nav li.dropdown > a:hover, .tw-bs .nav li.dropdown > a:focus { - background-color: #f4f4f4; - color: #222D32; - text-decoration: none; + background-color: #f4f4f4; + color: #222D32; + text-decoration: none; } /* Global Compliance */ @@ -386,34 +392,33 @@ a.sidebar-toggle{ /* ----- ----- */ /* -- ------------ */ .skin-yellow .sidebar-menu>li.footer>span{ - padding: 12px 5px 12px 15px; - display: block; + padding: 12px 5px 12px 15px; + display: block; } .skin-yellow .sidebar-menu>li.footer>a{ - color:#556D77; + color:#556D77; } .sidebar-menu li.treeview.active>a>.fa-angle-left { - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - -o-transform: rotate(90deg); - transform: rotate(90deg); + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + -o-transform: rotate(90deg); + transform: rotate(90deg); } .sidebar-menu li.treeview>a>.fa-angle-left { - -webkit-transform: rotate(-90deg); - -ms-transform: rotate(-90deg); - -o-transform: rotate(-90deg); - transform: rotate(-90deg); + -webkit-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + transform: rotate(-90deg); } - .skin-yellow .sidebar-menu>li.footer { - color: #556D77; - background: #1a2226; - position:absolute; - bottom:0; - width:230px; - overflow:hidden; + color: #556D77; + background: #1a2226; + position:absolute; + bottom:0; + width:230px; + overflow:hidden; } .skin-yellow.sidebar-collapse .sidebar-menu>li.footer{ -webkit-animation: expand .3s ease-in-out forwards; /* Safari 4+ */ @@ -427,6 +432,7 @@ a.sidebar-toggle{ -o-animation: squeeze .3s ease-in-out forwards; /* Opera 12+ */ animation: squeeze .3s ease-in-out forwards; /* IE 10+, Fx 29+ */ } + @-webkit-keyframes squeeze { 0% {visibility:hidden;} 99% {visibility:hidden;} @@ -468,32 +474,32 @@ a.sidebar-toggle{ 100% {visibility:hidden;} } .skin-yellow .sidebar-menu>li.footer:hover>a, .skin-yellow .sidebar-menu>li.footer.active>a { - border-left-color: transparent; - background: #1a2226; + border-left-color: transparent; + background: #1a2226; } .skin-yellow .sidebar-menu>li.footer>a:hover, .skin-yellow .sidebar-menu>li.footer.active>a:hover { - color: #fff; - background: #1e282c; + color: #fff; + background: #1e282c; } /* ----- ----- */ @media (max-width: 767px){ - .tw-bs .navbar-nav .open .dropdown-menu { - position: absolute; - right: 42px; - float: none; - width: 280px; - background-color: rgb(255, 255, 255); - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - } + .tw-bs .navbar-nav .open .dropdown-menu { + position: absolute; + right: 42px; + float: none; + width: 280px; + background-color: rgb(255, 255, 255); + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + } } .navbar-nav .notifications-menu>.dropdown-menu>li.footer>a{ - font-size:14px !important; + font-size:14px !important; } .navbar-nav .notifications-menu>.dropdown-menu>li.footer>a.regeneratePolicies{ color:#a94442 !important; @@ -507,17 +513,17 @@ a.sidebar-toggle{ border-bottom-color: #d9534f; } .tw-bs .main-header .navbar .nav li.notifications-menu> a{ - color:#fff; - height: 100%; + color:#fff; + height: 100%; } .tw-bs .navbar-nav li.dropdown { - height: 50px; + height: 50px; } .tw-bs .main-header .navbar .nav li.notifications-menu> a:hover .rudder-badge, .tw-bs .main-header .navbar .nav li.notifications-menu> a:focus .rudder-badge, .tw-bs .main-header .navbar .nav li.notifications-menu> a:active .rudder-badge, .tw-bs .main-header .navbar .nav li.notifications-menu> a:visited .rudder-badge{ - background-color:#fff; + background-color:#fff; } .tw-bs .main-header .navbar .nav a:hover, @@ -532,47 +538,47 @@ a.sidebar-toggle{ .main-header .navbar .nav>.active a, .main-header .navbar .nav li.dropdown > a:hover, .main-header .navbar .nav li.dropdown > a:focus{ - background: rgba(0,0,0,0.1); - color: #f6f6f6; + background: rgba(0,0,0,0.1); + color: #f6f6f6; } .notifications-menu .rudder-badge { - position: relative; - left: 7px; - top: -2px; - background-color: #fff; - border: 2px solid rgba(240, 128, 4, 0.47); - color: rgb(34, 45, 50); - border-radius:3px; + position: relative; + left: 7px; + top: -2px; + background-color: #fff; + border: 2px solid rgba(240, 128, 4, 0.47); + color: rgb(34, 45, 50); + border-radius:3px; } .notifications-menu .rudder-badge::before { - content: ""; - left: -9px; - top: 4px; - color: transparent; - width: 0; - height: 0; - border-top: 5px solid transparent; - border-bottom: 5px solid transparent; - border-right: 11px solid #FFF; - position: absolute; + content: ""; + left: -9px; + top: 4px; + color: transparent; + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 11px solid #FFF; + position: absolute; } .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a>.fa{ - width: 30px; - color: #222D32; - opacity: 0.4; + width: 30px; + color: #222D32; + opacity: 0.4; } .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a>.fa.text-succes{ - color: #3c763d; + color: #3c763d; } .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a>.fa.text-danger{ - color: #a94442; + color: #a94442; } .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a>.fa-flag-o, .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a>.fa-flag-checkered{ - font-size: 16px; + font-size: 16px; } .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a{ - font-size:12px !important; + font-size:12px !important; } .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:hover .fa, .navbar-nav .messages-menu>.dropdown-menu>li .menu>li>a:hover .fa, @@ -580,107 +586,1087 @@ a.sidebar-toggle{ .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:hover .label, .navbar-nav .messages-menu>.dropdown-menu>li .menu>li>a:hover .label, .navbar-nav .tasks-menu>.dropdown-menu>li .menu>li>a:hover .label{ - opacity: 1; + opacity: 1; } .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a.text-success, .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:hover.text-success, .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:focus.text-success, .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:visited.text-success{ - color: #3c763d !important; + color: #9bc832 !important; } .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a.text-danger, .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:hover.text-danger, .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:focus.text-danger, .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:visited.text-danger{ - color: #a94442 !important; + color: #a94442 !important; } .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:hover { - color: #444444 !important; + color: #444444 !important; } .navbar-nav .tasks-menu>.dropdown-menu>li .menu>li>a:hover{ - color:#444444; + color:#444444; } .navbar-nav .dropdown-menu>li .menu>li>a .label { - font-size: 90%; - background-color: #222D32; - opacity: 0.4; - position: absolute; - right: 10px; + font-size: 90%; + background-color: #222D32; + opacity: 0.4; + position: absolute; + right: 10px; } .navbar-nav li .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0!important; - border-top-right-radius: 0!important; - border-color:#E2E2E2; - border-bottom: none; + margin-top: 0; + border-top-left-radius: 0!important; + border-top-right-radius: 0!important; + border-color:#E2E2E2; + border-bottom: none; } .tw-bs .navbar-nav .notifications-menu>.dropdown-menu>li .menu>li>a:hover, .navbar-nav .messages-menu .dropdown-menu>li .menu>li>a:hover, .navbar-nav .tasks-menu .dropdown-menu>li .menu>li>a:hover { - background: #f4f4f4; + background: #f4f4f4; } .navbar-nav li>.dropdown-menu .menu{ - border-bottom: none; + border-bottom: none; } .main-header .navbar .nav li.dropdown>a>.label { - position: absolute; - top: 5px; - right: 23px; - text-align: center; - font-size: 9px; - padding: 2px 3px; - line-height: .9; + position: absolute; + top: 5px; + right: 23px; + text-align: center; + font-size: 9px; + padding: 2px 3px; + line-height: .9; } #generation-status{ - font-size: 14px; + font-size: 14px; + background-color: #9bc832 !important; } .tw-bs .main-header .navbar .nav li.notifications-menu> a .fa-heartbeat{ - font-size: 18px; + font-size: 18px; } .label-neutral{ - background-color: #ABACAC !important; + background-color: #ABACAC !important; } .tw-bs .main-header .navbar .nav li.notifications-menu> a #generation-status .fa{ - transition-duration:.2s; + transition-duration:.2s; } .no-click{ - cursor:default !important; + cursor:default !important; } .tw-bs .nav > li > a:hover .glyphicon, .tw-bs .nav > li > a:focus .glyphicon, .tw-bs .nav li.dropdown > a:hover .glyphicon, .tw-bs .nav li.dropdown > a:focus .glyphicon { - color: #FFFFFF; + color: #FFFFFF; } /* ----- ----- */ /* ----- ----- */ .rule-footer { - width: calc(100% - 230px); - left: 230px; - bottom: 0; + width: calc(100% - 230px); + left: 230px; + bottom: 0; } .skin-yellow.sidebar-collapse .rule-footer { - width: calc(100% - 50px); - left: 50px; + width: calc(100% - 50px); + left: 50px; } @media (max-width: 767px){ - .skin-yellow.sidebar-collapse .rule-footer,.skin-yellow .rule-footer { - width: 100%; - left: 0; - bottom: 0; - } + .skin-yellow.sidebar-collapse .rule-footer,.skin-yellow .rule-footer { + width: 100%; + left: 0; + bottom: 0; + } } .directive-footer { - width: 59.3%; - bottom: 0; - border-right: 0; + width: 59.3%; + bottom: 0; + border-right: 0; } .skin-yellow.sidebar-collapse .directive-footer { - width: 63.2%; + width: 63.2%; } /* --- DIRECTIVE PAGE --- */ #directiveDetails{ - border-left: 15px solid #ECF0F5; + border-left: 15px solid #ECF0F5; } #directiveDetails,.directiveTree{ - max-height:88vh; - overflow-y:auto; - overflow-x:hidden; + max-height:88vh; + overflow-y:auto; + overflow-x:hidden; + padding-bottom: 20px; +} +.ui-widget.ui-tooltip h4{ + margin-bottom: 10px; + font-size:16px; + padding-bottom:5px; + border-bottom : 1px solid #e6e6e6; +} +.ui-widget.ui-tooltip p{ + margin-bottom: 6px; +} + +.ui-widget.ui-tooltip ol{ + list-style-type:disc; + padding-left:30px; + margin-bottom:15px; +} +.ui-widget.ui-tooltip li{ + list-style-type:disc; +} +.ui-widget.ui-tooltip li b{ + color : #f08004; +} + +/* --- --- */ +.text-warning-rudder{ + color: #f08004; +} +.text-danger-rudder{ + color: #C50000; +} +.text-info-rudder{ + color: #1b809e; +} +.space-top{ + margin-top: 20px; +} +.space-bottom{ + margin-bottom: 30px; +} +.padding-col{ + margin-left: 15px; +} + +.first-index{ + z-index: 500; +} + +.display-none{ + display: none; +} + +.callout-fade ol,.callout-fade ul{ + margin-bottom: 10px; +} +.callout-fade ul{ + margin-left: 25px; +} +.callout-fade ol{ + margin-left: 50px; +} +.callout-fade ol,.callout-fade ol li{ + list-style-type: decimal; +} +.callout-fade ul,.callout-fade ul li{ + list-style-type: disc; +} +/* --- LABEL --- */ +#badge-apm{ + margin-left: 5px; +} +#reportsGrid .tooltip, #nodeReportsGrid>tbody>tr>td>.tooltip{ + left:-42px !important; +} +#reportsGrid .tooltip.top > .tooltip-arrow, #nodeReportsGrid>tbody>tr>td>.tooltip.top > .tooltip-arrow,#groupTree ul.jstree-container-ul>li>ul>li>a .tooltip.top > .tooltip-arrow{ + left: 38% !important; +} +#groupTree ul.jstree-container-ul>li>ul>li>a .tooltip{ + left:0 !important; +} + +.tw-bs .tooltip{ + z-index:99999999999; +} +.tw-bs .tooltip > .tooltip-inner { + background-color: #fff; + padding: 0 !important; + color: #222D32; + text-align: left !important; + width: 280px; + max-width: 280px; + font-size: 12px; + box-shadow: 0px 0px 5px rgba(0, 0, 0, .12); + border-radius: 0px; + color: #222D32; + border: 1px solid #ccc; + border-radius: 3px; +} +.tw-bs .tooltip.top > .tooltip-arrow{ + border-top-color: #ccc; +} +.tw-bs .tooltip > .tooltip-inner h4{ + background-color: #fff; + font-weight:600; + font-sive:20px; + color: #333; + border-bottom: 1px solid #ccc; + padding: 5px 10px 5px 5px; + font-size: 14px; + margin-left: 3%; + margin-right: 3%; + width: 94%; +} +form .tooltip-content p { + padding:0; +} +.tw-bs .tooltip > .tooltip-inner .tooltip-content{ + padding: 0px 10px 10px 10px; +} +.tw-bs .tooltip > .tooltip-inner .tooltip-content i b{ + color:#333; + background: none; + -webkit-background-clip: initial; + -webkit-text-fill-color: initial; +} +.tw-bs .tooltip > .tooltip-inner .tooltip-content p{ + margin-bottom: 5px; +} +.tw-bs .tooltip.in { + opacity: 1; + filter: alpha(opacity=100); +} +.tw-bs .rudder-label{ + cursor: help; + display: inline-block; + text-align: center; + color: #fff; + padding: 5px 15px; + font-weight: 700; + border-radius: 3px; + min-width: 78px; +} +.dataTable td.tw-bs,.dataTable td .tw-bs{ + background-color:transparent; +} +.text-align-center{ + text-align:center; +} +.tw-bs .jstree-container-ul .rudder-label.label-sm,.dataTable .rudder-label.label-sm{ + padding: 0 !important; + min-width: 45px !important; + margin-right: 6px; + position: relative; + top: -2px; + height: 18px; + line-height: 18px; + font-style:normal !important; +} +#nodeDetails .rudder-label{ + position: relative; + margin-bottom: -4px; + top: -4px; + padding: 1px 8px; +} + +.rudder-label.label-md{ + font-size: 12px; + padding: 4px 12px; + min-width: 68px; +} +.tw-bs .rudder-label.label-sm{ + font-size: 10px; + padding: 3px 8px; + min-width: 50px; +} +.tw-bs .rudder-label.label-verify{ + background-color: #3694d1; +} + +.tw-bs .rudder-label.label-verify:before,.tw-bs .label-text.label-verify:after{ + content: "Audit"; +} +.tw-bs .rudder-label.label-verify + .tooltip b, .tw-bs .label-text.label-verify:after, .tw-bs .label-text.label-verify + .tooltip b, .text-Audit{ + color: #3694d1; +} +.tw-bs .rudder-label.label-enforce{ + background-color: #9BC832; } +.tw-bs .rudder-label.label-enforce:before,.tw-bs .label-text.label-enforce:after{ + content: "Enforce"; +} +.tw-bs .rudder-label.label-enforce + .tooltip b, .tw-bs .label-text.label-enforce:after, .tw-bs .label-text.label-enforce + .tooltip b, .text-Enforce{ + color: #9BC832; +} +.tw-bs .rudder-label.label-mixed{ + background: rgb(155,200,50); + background: -moz-linear-gradient(-45deg, rgba(155,200,50,1) 0%, rgba(155,200,50,1) 50%, rgba(54,148,209,1) 50%, rgba(54,148,209,1) 100%); + background: -webkit-linear-gradient(-45deg, rgba(155,200,50,1) 0%,rgba(155,200,50,1) 50%,rgba(54,148,209,1) 50%,rgba(54,148,209,1) 100%); + background: linear-gradient(-45deg, rgba(155,200,50,1) 0%,rgba(155,200,50,1) 50%,rgba(54,148,209,1) 50%,rgba(54,148,209,1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9bc832', endColorstr='#3694d1',GradientType=1 ); +} +.tw-bs .rudder-label.label-mixed:before,.tw-bs .label-text.label-mixed:after{ + content: "Mixed"; +} +.tw-bs .rudder-label.label-mixed + .tooltip b, .tw-bs .label-text.label-mixed:after, .tw-bs .label-text.label-mixed + .tooltip b{ + font-weight: bold; +} +.tw-bs .rudder-label.label-error{ + background-color: #C00804; +} +.tw-bs .rudder-label.label-error:before,.tw-bs .label-text.label-error:after{ + content: "Error"; +} +.tw-bs .rudder-label.label-error + .tooltip b, .tw-bs .label-text.label-error:after, .tw-bs .label-text.label-error + .tooltip b, .text-Error{ + color: #C00804; +} +.tw-bs .label-text{ + color: #999; + float: right; + width: 100%; + text-align: right; +} +.tw-bs .label-text:after{ + float: left; + font-family: Verdana,Arial,sans-serif; +} + +/* +* CALLOUT-FADE +*/ +/* --- GENERAL --- */ +.callout-fade{ + padding-right: 30px !important; + font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,Arial,sans-serif; + font-size:14px; + background-color: #fff; + padding: 10px 15px; + margin-bottom: 20px; + border: 1px solid #eee; + border-left-width: 5px; + border-radius: 3px; + box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.18); + border-left-color: #818181; + overflow: hidden; +} +.callout-fade h4{ + font-size: 25px; + margin-top: 0; + margin-bottom: 12px; + color: #818181; +} +.callout-fade p { + margin: 0 0 10px; +} +.callout-fade p:last-child { + margin-bottom: 0; +} +.callout-fade div.marker{ + position: absolute; + top: -30px; + right: -30px; + width: 60px; + height: 60px; + background-color: #818181; + color: white; + text-align: center; + line-height: 96px; + box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.18); + transform: rotate(45deg); +} +.callout-fade div.marker span.glyphicon{ + transform:rotate(-45deg); + transition-duration:.2s; +} +.callout-fade:hover div.marker span.glyphicon{ + transform:rotate(-65deg); +} +/* --- SUCCESS --- */ +.callout-fade.callout-success{ + border-left-color: #9bc832; +} +.callout-fade.callout-success h4,.callout-fade.callout-success b{ + color: #9bc832; +} +.callout-fade.callout-success div.marker{ + background-color: #9bc832; +} +/* --- INFO --- */ +.callout-fade.callout-info{ + border-left-color: #1b809e; +} +.callout-fade.callout-info h4,.callout-fade.callout-info b{ + color: #1b809e; +} +.callout-fade.callout-info div.marker{ + background-color: #1b809e; +} +/* --- WARNING --- */ +.callout-fade.callout-warning{ + border-left-color: #f08004; +} +.callout-fade.callout-warning h4,.callout-fade.callout-warning b{ + color: #f08004; +} +.callout-fade.callout-warning div.marker{ + background-color: #f08004; +} +/* --- DANGER --- */ +.callout-fade.callout-danger{ + border-left-color: #C50000; +} +.callout-fade.callout-danger h4,.callout-fade.callout-danger b{ + color: #C50000; +} +.callout-fade.callout-danger div.marker{ + background-color: #C50000; +} +/* +*----------------------------------------- +*/ +/* +* INPUT GROUP GENERAL +*/ +.group-button button.btn{ + color: white; + border-right: none; +} +.group-button input.form-control, .group-button button.btn{ + transition-duration:.2s; +} +.group-button input.form-control:focus, .group-button button.btn:hover{ + box-shadow: 0 0 4px rgba(0, 0, 0, 0.43); + border-color: transparent; +} +.group-button button.btn:hover{ + +} +/* PLUS */ +.group-button.group-plus button.btn{ + background-color: #9bc832; +} +/* MINUS */ +.group-button.group-minus button.btn{ + background-color: #C50000; +} +/* WARNING */ +.group-button.group-warning button.btn{ + background-color: #f08004; +} +/* +*----------------------------------------- +*/ +/* +* SMALL BOX BG-FADE +*/ +.small-box.bg-fade{ + background-color: #fff; + color:rgba(0,0,0,0.8); + box-shadow: 0px 10px 10px -8px rgba(0, 0, 0, 0.18); +} +.small-box.bg-fade-1 .icon { + color:#9bc832; +} +.small-box.bg-fade-1 .small-box-footer { + background-color: #9bc832; +} + .small-box.bg-fade-2 .icon { + color:#1b809e; +} +.small-box.bg-fade-2 .small-box-footer { + background-color: #1b809e; +} + .small-box.bg-fade-3 .icon { + color:#f08004; +} +.small-box.bg-fade-3 .small-box-footer { + background-color: #f08004; +} + .small-box.bg-fade-4 .icon { + color:#c50000 ; +} +.small-box.bg-fade-4 .small-box-footer { + background-color: #c50000; +} + .small-box.bg-fade-5 .icon { + color:#818181; +} +.small-box.bg-fade-5 .small-box-footer { + background-color: #818181; +} + .small-box.bg-fade-6 .icon { + color:#222d32; +} +.small-box.bg-fade-6 .small-box-footer { + background-color: #222d32; +} + /* +*----------------------------------------- +*/ +.no-padding{ + padding: 0 !important; +} +.page.exemple{ + min-height: 900px; +} +.page{ + background-color: #fff; + padding-top: 20px; + padding-bottom: 50px; +} +.column .inner-portlet { + margin: 7px 10px 10px 10px; +} +.inner-portlet .page-title,.portlet-content .page-title { + color: #222D32; + border-bottom: 1px solid #f08004; + box-shadow: 0px 12px 10px -10px rgba(0, 0, 0, 0.1); + padding-bottom: 4px; + margin: 5px 0 25px; + font-size: 18px; + font-weight:500; +} +.page div.row{ + margin-right: 15px; + margin-left: 15px; +} + +/* ------------- FORM --------------- */ +form.rudder-form .form-group { + margin-bottom: 0; +} +form.rudder-form{ + box-shadow: 0px 10px 10px -10px rgba(0, 0, 0, 0.18); + margin-bottom: 15px; + padding-bottom: 20px; +} +form.rudder-form.no-shadow{ + box-shadow: none; + padding-bottom: 20px; +} +form.rudder-form.bordered{ + border: 3px solid rgba(118, 118, 118, 0.11); + box-shadow: 0px 10px 8px -2px rgba(0, 0, 0, 0.08); +} +form.rudder-form legend{ + padding-left: 15px; + padding-bottom: 5px; + padding-top: 5px; +} +form.rudder-form textarea.form-control{ + resize:vertical; + min-height: 80px; +} +.policymode-group{ + margin-bottom:10px; +} +form.rudder-form .form-group label,.policymode-group.node{ + margin-bottom: 4px; +} + +form.rudder-form .info-mode{ + margin-left: 10px; + position:relative; + top:1px; +} +form.rudder-form .form-control{ + border-radius: 3px; + border-color: rgb(198, 200, 201); + box-shadow: 0 0 2px #C1BFBF; +} +form.rudder-form .form-control:focus,.rudder-form .form-group.has-error .form-control:focus{ + box-shadow: 0 0 6px #C1BFBF; +} +form.rudder-form .btn-group .btn{ + min-width: 85px; +} +form.rudder-form .policymode-group .btn-group .btn,form.rudder-form .btn-group.yesno .btn{ + font-size:14px; +} +form.rudder-form .policymode-group .btn-group .btn .mode{ + color: #3694d1; + top: 2px; + right: -2px; +} +form.rudder-form .policymode-group .btn-group .btn .mode.enforce{ + color: #9bc832; +} +form.rudder-form .policymode-group .btn-group .btn .mode:before{ + content: "("; + color: #757575; + margin: 0 0 0 2px; +} +form.rudder-form .policymode-group .btn-group .btn .mode:after{ + content: ")"; + color: #757575; + margin: 0 2px 0 0; +} +form.rudder-form .policymode-group .btn-group .btn.audit.active,form.rudder-form .policymode-group .btn-group .btn.enforce.active{ + color:#fff; + webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.225); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.225); +} +form.rudder-form .policymode-group .btn-group .btn.audit.active{ + background-color: #3694d1; +} +form.rudder-form .policymode-group .btn-group .btn.enforce.active{ + background-color: #9bc832; +} + +.group-help .input-group-btn .btn-check{ + width: 40px; + position: relative; + top: 7px; + left: 1.5px; + display: inline-block; +} +.group-help{ + border-bottom: 1px solid #222D32; + margin-bottom: 10px; +} +.group-help a.btn{ + background-color: #EDEDED; + color: #222D32; + margin-bottom: -1px; + border-bottom-left-radius: 0; + outline: none; + border-color: #222D32; + transition-property: background-color; + transition-duration: .1s; +} +.group-help a.btn span{ + transition-duration:.1s; + transition-timing-function: ease-in; +} + +.group-help a.btn:hover{ + color: #fff; + outline: none; + background-color: #222D32; + +} +.group-help .check-icon{ + position: absolute; + right: 0; + bottom: 9px; + visibility:hidden; + animation: opacity-0 .2s linear forwards; +} +.group-help .check-icon.visible{ + visibility:visible; + animation: opacity-1 .2s linear forwards; +} + +/* BUTTON-GROUP-ERROR */ +.button-group-error .button-error{ + visibility:hidden; + animation: opacity-0 .2s linear forwards; + background-color: #c50000; + display: inline-block; + color: #fff; + margin-bottom: 4px; + padding: 6px 12px; + margin-left: -15px; + border-radius: 3px; + z-index: 1; + +} +.button-group-error .button-error:before{ + width: 0; + height: 0; + border-left: 12px solid transparent; + border-right: 12px solid transparent; + border-top: 14px solid #C50000; + display: inline-block; + content: " "; + position: absolute; + z-index: 1; + top: 30px; +} +.button-group-error .button-error.error{ + visibility:visible; + animation: opacity-1 .2s linear forwards; +} +/* BUTTON-GROUP-SUCCESS */ +.rudder-form .button-group-success{ + margin-top: 15px; +} +.close-message.glyphicon { + color: rgba(0, 0, 0, .3); + top: 2px; + left: 2px; + margin-left: 10px; + cursor: pointer; +} +.button-group-success .button-success,.button-group-success .button-warning{ + opacity:0; + background-color: #9bc832; + display: inline-block; + color: #fff; + margin-bottom: 4px; + padding: 5px 12px; + margin-left: -15px; + border-radius: 3px; +} +.button-group-success .button-success.ngHide{ + visibility:hidden; +} +.button-warning.no-modif,.button-group-success .button-success{ + top: 1.5px; + position: relative; + margin-left:2px; +} +.button-group-success .button-warning.no-modif:before,.button-group-success .button-success:before{ + width: 0; + height: 0; + border-left: none; + border-right: none; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + border-right: 12px solid #b1b1b1; + display: inline-block; + content: " "; + position: absolute; + z-index: 1; + top: 6px; + left: -10px; +} +.button-group-success .button-success:before{ + border-right: 12px solid #9bc832; +} + +.button-group-success .button-warning{ + background-color: #b1b1b1; +} +.button-shape{ + display:inline-block; +} +.button-shape.disabled{ + cursor:no-drop; +} +.button-group-success .button-warning:before{ + width: 0; + height: 0; + border-left: 12px solid transparent; + border-right: 12px solid transparent; + border-top: 14px solid #9bc832; + display: inline-block; + content: " "; + position: absolute; + z-index: 1; + top: 26px; +} +.button-group-success .button-success.error{ + background-color:#C50000; +} +.button-group-success .button-success.error:before{ + border-top-color: #C50000; +} +.button-group-success .button-success:before{ + visibility:hidden; +} +.button-group-success .button-success.pop:before{ + visibility:visible; +} +.button-group-success .button-warning:before{ + border-top: 14px solid #F08004; +} +.button-group-success .pop.button-warning:before{ +} +.button-group-success .button-success.pop,.button-group-success .button-warning.pop{ + visibility:visible; + animation: opacity-1 .3s linear forwards; +} + +/* WITH ERRORS */ + +.rudder-form .form-group.has-error label { + color: #C50000; +} +.rudder-form .form-group.has-error .form-control { + border-color: rgb(198, 200, 201); + box-shadow: 0 0 2px #C1BFBF; +} +.rudder-form .help-block{ + visibility:hidden; + animation: opacity-0 .2s linear forwards; +} +.rudder-form .has-error .help-block{ + visibility:visible; + animation: opacity-1 .3s linear forwards; + background-color: #C50000; + color: #fff; + padding: 6px 12px; + border-radius: 3px; + margin-bottom: 0; +} +.rudder-form .has-error .help-block:before{ + width: 0; + height: 0; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-bottom: 12px solid #C50000; + display: inline-block; + content: " "; + position: absolute; + z-index: 1; + top: 52px; +} +.rudder-form .has-feedback label~.form-control-feedback { + top: 23px; + color:#C50000; + visibility:hidden; + animation: opacity-0 .2s linear forwards; +} +.rudder-form .has-error.has-feedback label~.form-control-feedback { + visibility:visible; + animation: opacity-1 .3s linear forwards; +} +/* SUCCESS FORM MESSAGE */ + +.rudder-form .validation-form-message{ + text-align: right; + padding-right: 4px; + z-index: 500; +} +.rudder-form .validation-form-message .message{ + visibility:hidden; + animation: opacity-0 .2s linear forwards; + display: inline-block; + color: #fff; + margin-bottom: 4px; + padding: 6px 18px; + margin-left: -15px; + border-radius: 3px; + border: 2px solid transparent; +} +.message.message-success{ + background-color: #9bc832; +} +.message.message-warning{ + background-color: #f08004; +} +.rudder-form .validation-form-message .message.success{ + visibility:visible; + animation: opacity-1 .3s linear forwards; +} +.rudder-form .validation-form-message .message:after { + width: 0; + height: 0; + border-top: 12px solid transparent; + border-bottom: 12px solid transparent; + display: inline-block; + content: " "; + position: absolute; + z-index: 1; + top: 7px; + right:-7px; +} +.rudder-form .validation-form-message .message.message-success.success:after { + border-left: 14px solid #9bc832; +} +.rudder-form .validation-form-message .message.message-warning.success:after { + border-left: 14px solid #f08004 ; +} + +/* RADIO BUTTONS + CHECKBOX */ + +.rudder-form input[type=radio],.rudder-form input[type=checkbox]{ + display: none; +} +.rudder-form input[type=radio]:disabled + .label-radio,.rudder-form input[type=checkbox]:disabled + .label-radio{ + background-color: rgb(236,236,236); +} +.rudder-form input[type=radio]:disabled + .label-radio span,.rudder-form input[type=checkbox]:disabled + .label-radio span{ + color:#898989; +} +.rudder-form input[type=radio]:checked:disabled + .label-radio + .text-radio, .rudder-form input[type=checkbox]:checked:disabled + .label-radio + .text-radio,.rudder-form input[type=radio]:disabled + .label-radio + .text-radio, .rudder-form input[type=checkbox]:disabled + .label-radio + .text-radio{ + color: #B7B4B4; + -webkit-animation:none; + -moz-animation:none; + -o-animation:none; + animation:none; +} +.rudder-form label.fit{ + font-weight: 500; +} +.rudder-form .sublabel{ + font-weight: 600; + font-size: 15px; + color: #222D32; + margin-bottom: 4px; +} +.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary, .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary { + color: #fff; + background: #f08004; +} +.label-switch{ + margin-left: 8px; +} +.bootstrap-switch.bootstrap-switch-focused { + border-color: #ccc; + outline: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.rudder-form input[type=radio] + .label-radio,.rudder-form input[type=checkbox] + .label-radio{ + display: inline-block; + height: 15px; + width: 15px; + border: 2px solid rgb(198, 200, 201); + text-align: center; + line-height: 34px; + position: relative; + top: 5px; +} +.rudder-form input[type=radio] + .label-radio{ + border-radius: 50%; +} +.rudder-form input[type=radio] + .label-radio + .text-radio,.rudder-form input[type=checkbox] + .label-radio + .text-radio{ + -webkit-animation: text-radio-off .2s ease-in forwards; + -moz-animation: text-radio-off .2s ease-in forwards; + -o-animation: text-radio-off .2s ease-in forwards; + animation: text-radio-off .2s ease-in forwards; +} +.rudder-form input[type=radio]:checked + .label-radio + .text-radio,.rudder-form input[type=checkbox]:checked + .label-radio + .text-radio{ + -webkit-animation: text-radio-on .2s ease-in forwards; + -moz-animation: text-radio-on .2s ease-in forwards; + -o-animation: text-radio-on .2s ease-in forwards; + animation: text-radio-on .2s ease-in forwards; +} +.rudder-form input[type=radio] + .label-radio span,.rudder-form input[type=checkbox] + .label-radio span{ + font-size: 9px; + top: -12.5px; + left: 0.1px; + position: relative; + visibility:hidden; + animation: opacity-0 .2s linear forwards; +} +.rudder-form input[type=radio]:checked + .label-radio span,.rudder-form input[type=checkbox]:checked + .label-radio span{ + visibility:visible; + animation: opacity-1 .2s linear forwards; +} +.rudder-form .text-radio{ + position: relative; + top: -6px; + margin-left: 8px; +} + +/* CHECKBOX */ +.rudder-form input[type=checkbox] + .label-radio{ + border-radius: 3px; +} + +/* HR */ +hr.separation{ + width: 100%; + height: 100px; + background-color: #f08004; + margin-top: 80px; + margin-bottom: 20px; +} + +/* POPOVER */ +.skin-yellow .popover-title { + background-color: #222D32; + border-bottom: 1px solid #222d32; + color: #fff; +} +.skin-yellow .popover-title span.ion{ + position: absolute; + right: 12px; + font-size: 20px; + top: 5.5px; +} +.skin-yellow .popover { + padding: 0; + font-family: inherit; + background-color: #fff; + border: 1px solid rgba(34, 45, 50, 0.44); +} + +/* ----------------- BUTTONS ----------------- */ +.btn span.ion{ + position: absolute; + right: 10px; + top: 10px; + visibility:hidden; + animation: opacity-0 .2s linear forwards; +} +.btn:hover span.ion{ + visibility:visible; + animation: opacity-1 .2s linear forwards; +} +.btn:focus span.ion{ + visibility:visible; + animation: opacity-1 .2s linear forwards; +} +/* BUTTON PRIMARY */ +.tw-bs .btn-primary-rudder:disabled { + cursor:not-allowed; +} +.tw-bs .btn-primary-rudder.loading>span:last-child { + -webkit-animation: loader .1s linear forwards; + -moz-animation: loader .1s linear forwards; + -o-animation: loader .1s linear forwards; + animation: loader .1s linear forwards; +} +.tw-bs .btn-primary-rudder .fa-spin{ + display: none; +} +.tw-bs .btn-primary-rudder.loading .fa-spin{ + display: inline; +} +.tw-bs .btn-primary-rudder { + outline: none !important; +} + +/* SPINNERS */ +.btn i.fa-spin { + position: absolute; + left: 14px; + font-size: 20px; +} +@keyframes opacity-1 { + 0% {opacity:0;visibility: visible;} + 100% {opacity:1;visibility: visible;} +} +@keyframes opacity-0 { + 0% {opacity:1;visibility: visible;} + 99% {opacity:0;visibility: visible;} + 100% {opacity:0;visibility: hidden;} +} +@-webkit-keyframes text-radio-on { + 0% {color: #646464;} + 100% {color: #222D32;} +} +@-moz-keyframes text-radio-on { + 0% {color: #646464;} + 100% {color: #222D32;} +} +@-o-keyframes text-radio-on { + 0% {color: #646464;} + 100% {color: #222D32;} +} +@keyframes text-radio-on { + 0% {color: #646464;} + 100% {color: #222D32;} +} +@-webkit-keyframes text-radio-off { + 0% {color: #222D32;} + 100% {color: #646464;} +} +@-moz-keyframes text-radio-off { + 0% {color: #222D32;} + 100% {color: #646464;} +} +@-o-keyframes text-radio-off { + 0% {color: #222D32;} + 100% {color: #646464;} +} +@keyframes text-radio-off { + 0% {color: #222D32;} + 100% {color: #646464;} +} +@-webkit-keyframes loader { + 0% {margin-left: 0px;} + 100% {margin-left: 25px;} +} +@-moz-keyframes loader { + 0% {margin-left: 0px;} + 100% {margin-left: 25px;} +} +@-o-keyframes loader { + 0% {margin-left: 0px;} + 100% {margin-left: 25px;} +} +@keyframes loader { + 0% {margin-left: 0px;} + 100% {margin-left: 25px;} +} \ No newline at end of file diff --git a/rudder-web/src/main/webapp/style/rudder/rudder-progress-bar.css b/rudder-web/src/main/webapp/style/rudder/rudder-progress-bar.css index d6a849ee68..84efc82ce3 100644 --- a/rudder-web/src/main/webapp/style/rudder/rudder-progress-bar.css +++ b/rudder-web/src/main/webapp/style/rudder/rudder-progress-bar.css @@ -46,7 +46,7 @@ background-color: #8dcd8d; } .tw-bs .progress-bar-success { - background-color: #5cb85c; + background-color: #9bc832; } .tw-bs .progress-bar-repaired { background-color: #FFFF66; @@ -61,7 +61,7 @@ background-color: #b4b4b4; } .tw-bs .progress-bar-unknown { - background-color: #FF6600; + background-color: #ff7125; } .tw-bs .progress-bar-missing { background-color: #FF9147; diff --git a/rudder-web/src/main/webapp/style/rudder/rudder.css b/rudder-web/src/main/webapp/style/rudder/rudder.css index 328477e908..8f890f5903 100644 --- a/rudder-web/src/main/webapp/style/rudder/rudder.css +++ b/rudder-web/src/main/webapp/style/rudder/rudder.css @@ -93,6 +93,9 @@ div.innerDetails { width:50%; padding:10px; } +#node_parameters{ + padding: 10px; +} table.nomargin { margin: 0px; @@ -421,7 +424,9 @@ Tableau #grid_rules_grid_zone { width:100%; } - +#grid_rules_grid_zone .tw-bs a{ + color:#333; +} #acceptNodeGrid_wrapper { margin: 10px; } @@ -506,7 +511,7 @@ table.dataTable td img, table.dataTable th img { } .field_errors, .error, .errors { - color: red; + color: #c00800; } .field_errors { @@ -2150,10 +2155,17 @@ div.topQuickSearchResults.ac_results { } .treeActiveTechniqueName , .treeActiveTechniqueCategoryName , .treeGroupCategoryName, .treeRuleCategoryName { - font-weight: 600; color: #555 } +span.treeDirective.tooltipable { + color: #555; + font-weight: 600; +} + +i.jstree-icon.jstree-themeicon.fa.fa-file-text.jstree-themeicon-custom { + color: #555; +} li.disableTreeNode > a.jstree-anchor { color: #999; diff --git a/rudder-web/src/main/webapp/templates-hidden/common-layout.html b/rudder-web/src/main/webapp/templates-hidden/common-layout.html index 6e2b17eae3..18a4bf8801 100644 --- a/rudder-web/src/main/webapp/templates-hidden/common-layout.html +++ b/rudder-web/src/main/webapp/templates-hidden/common-layout.html @@ -18,6 +18,7 @@ + @@ -29,7 +30,6 @@ - diff --git a/rudder-web/src/main/webapp/templates-hidden/components/ComponentAgentPolicyMode.html b/rudder-web/src/main/webapp/templates-hidden/components/ComponentAgentPolicyMode.html new file mode 100644 index 0000000000..29296bdc46 --- /dev/null +++ b/rudder-web/src/main/webapp/templates-hidden/components/ComponentAgentPolicyMode.html @@ -0,0 +1,112 @@ + +
+
+
+

Agent Policy Mode

+
+
+
+
+ +
+

+ Configuration rules in Rudder can operate in one of two modes: +

+
    +
  1. Audit: the agent will examine configurations and report any differences, but will not make any changes
  2. +
  3. Enforce: the agent will make changes to fix any configurations that differ from your directives
  4. +
+

+ This setting is a global switch and will apply to all nodes and all directives as the default mode. You may also override this mode on a per-node and per-directive basis. +

+

+ By default all nodes and all directives operate in the global mode defined in Settings + , which is currently {{globalConfiguration.policyMode}}. +

+
+
+
+

Global policy mode

+

Override policy mode for this node

+
+
+ + + +
+ + + This may be overriden on a per-Directive basis. + + + Directives will never be enforced on this node, regardless of their policy mode. + + + All Directives will apply necessary changes on this node, except Directives with an Audit override setting. + + +
+
+

Allow overrides on this default setting?

+
+
+ + +
+ + +
+
+
+ + + + + + There are no modifications to save. + + + Your changes have been saved + Error during saving changes + + + +
+
+ +
+
+
+ + + + diff --git a/rudder-web/src/main/webapp/templates-hidden/components/ComponentAgentSchedule.html b/rudder-web/src/main/webapp/templates-hidden/components/ComponentAgentSchedule.html index 030845a321..4b0261032c 100644 --- a/rudder-web/src/main/webapp/templates-hidden/components/ComponentAgentSchedule.html +++ b/rudder-web/src/main/webapp/templates-hidden/components/ComponentAgentSchedule.html @@ -12,8 +12,12 @@
Agent run schedule
-
-
+
+
+
+ +
+

By default, the agent runs on all nodes every 5 minutes.

This high frequency enables fast response times to apply changes and state @@ -29,8 +33,8 @@

The current global setting is to run every {{getIntervalValue(globalRun.interval)}}, starting at {{(format2Digits(globalRun.startHour))}}:{{(format2Digits(globalRun.startMinute))}}, with a maximum delay after scheduled run time (random interval) of {{(format2Digits(globalRun.splayHour))}}:{{(format2Digits(globalRun.splayMinute))}}.

You may override this global setting just for this node below:

+
-
diff --git a/rudder-web/src/main/webapp/templates-hidden/components/ComponentComplianceMode.html b/rudder-web/src/main/webapp/templates-hidden/components/ComponentComplianceMode.html index d56311e9f2..575986f7eb 100644 --- a/rudder-web/src/main/webapp/templates-hidden/components/ComponentComplianceMode.html +++ b/rudder-web/src/main/webapp/templates-hidden/components/ComponentComplianceMode.html @@ -4,22 +4,32 @@
Compliance reporting mode
-
-
- This setting affects the reports sent from each agent to this central server.
- In Full compliance mode, a report will be sent for each configuration component that is checked, even if no - changes were necessary (these are known as 'success' reports). This mode is much more verbose, in terms of logs and - network traffic, but provides more precise reporting and may be necessary to prove compliance in your organization.
- In Changes only mode, reports will only be sent when the agent makes a change or an error occurs on a node - (these are 'repair' or 'error' reports). This mode saves a lot of log space and bandwidth, but leads to some assumptions - about actual configuration status in reporting.
- In Disabled mode, no reports will be sent, and rudder-agent will not re-configure the local syslog to send reports. - This mode uses no log space or bandwidth, but will also not allow you to check if your configuration policy - is successfully applied. We do not recommend using this mode except for setups where you have another - feedback mechanism in place. +
+
+
+ +
+ This setting affects the reports sent from each agent to this central server. +
    +
  • + In Full compliance mode, a report will be sent for each configuration component that is checked, even if no + changes were necessary (these are known as 'success' reports). This mode is much more verbose, in terms of logs and + network traffic, but provides more precise reporting and may be necessary to prove compliance in your organization. +
  • +
  • + In Changes only mode, reports will only be sent when the agent makes a change or an error occurs on a node + (these are 'repair' or 'error' reports). This mode saves a lot of log space and bandwidth, but leads to some assumptions + about actual configuration status in reporting. +
  • +
  • + In Disabled mode, no reports will be sent, and rudder-agent will not re-configure the local syslog to send reports. + This mode uses no log space or bandwidth, but will also not allow you to check if your configuration policy + is successfully applied. We do not recommend using this mode except for setups where you have another + feedback mechanism in place. +
  • +
-
@@ -61,7 +71,7 @@ In "changes only" compliance mode, the agent will report a "heartbeat" when no other events are reported (no errors and no repairs). This frequency can be changed to send a heartbeat only every N runs.
This setting is defined as a default for all nodes in the global - Settings.
+ Settings.
The current global setting is to run every {{globalValue.heartbeatPeriod}} runs, (so every {{globalValue.heartbeatPeriod * agentRun}} minutes).
You may override this global setting just for this node below: diff --git a/rudder-web/src/main/webapp/templates-hidden/components/ComponentDirectiveEditForm.html b/rudder-web/src/main/webapp/templates-hidden/components/ComponentDirectiveEditForm.html index 2ea055646a..61ea55563e 100644 --- a/rudder-web/src/main/webapp/templates-hidden/components/ComponentDirectiveEditForm.html +++ b/rudder-web/src/main/webapp/templates-hidden/components/ComponentDirectiveEditForm.html @@ -75,12 +75,16 @@

Technique version deprecated

Here come the priority field

+
+ Here come the policy mode field +
+
Here come the version field

- Here come the version field + Here come the migrate field