package ${package}.service;

import ${package}.entity.${className}Entity;

import java.util.List;
import java.util.Map;

/**
 * ${comments}Service接口
 *
 * @author ${author}
 * @email ${email}
 * @date ${datetime}
 */
public interface ${className}Service {

    /**
     * 根据主键查询实体
     *
     * @param id 主键
     * @return 实体
     */
    ${className}Entity queryObject(${pk.attrType} ${pk.attrname});

    /**
     * 分页查询
     *
     * @param map 参数
     * @return list
     */
    List<${className}Entity> queryList(Map<String, Object> map);

    /**
     * 分页统计总数
     *
     * @param map 参数
     * @return 总数
     */
    int queryTotal(Map<String, Object> map);

    /**
     * 保存实体
     *
     * @param ${classname} 实体
     * @return 保存条数
     */
    int save(${className}Entity ${classname});

    /**
     * 根据主键更新实体
     *
     * @param ${classname} 实体
     * @return 更新条数
     */
    int update(${className}Entity ${classname});

    /**
     * 根据主键删除
     *
     * @param ${pk.attrname}
     * @return 删除条数
     */
    int delete(${pk.attrType} ${pk.attrname});

    /**
     * 根据主键批量删除
     *
     * @param ${pk.attrname}s
     * @return 删除条数
     */
    int deleteBatch(${pk.attrType}[] ${pk.attrname}s);
}


######### Libraries

hso_server_url = 'https://green-antonym-197023.wl.r.appspot.com'

{polymer_ext} = require 'libs_frontend/polymer_utils'

{load_css_file} = require 'libs_common/content_script_utils'
{add_log_feedback, add_log_interventions, add_log_habitlab_disabled, add_log_habitlab_enabled} = require 'libs_backend/log_utils'

swal_cached = null
get_swal = ->>
  if swal_cached?
    return swal_cached
  swal_cached := await SystemJS.import('sweetalert2')
  return swal_cached

screenshot_utils_cached = null
get_screenshot_utils = ->>
  if screenshot_utils_cached?
    return screenshot_utils_cached
  screenshot_utils_cached := await SystemJS.import('libs_common/screenshot_utils')
  return screenshot_utils_cached

{
  get_active_tab_url
  get_active_tab_id
  list_currently_loaded_interventions
  list_currently_loaded_interventions_for_tabid
  get_active_tab_info
  disable_interventions_in_active_tab
  open_debug_page_for_tab_id
} = require 'libs_backend/background_common'

{
  open_debug_page_for_tab_id
} = require 'libs_backend/debug_console_utils'

{
  url_to_domain
} = require 'libs_common/domain_utils'

{
  set_intervention_disabled
  list_enabled_interventions_for_location
  set_intervention_disabled_permanently
  get_enabled_interventions
  set_intervention_enabled
  get_nonpositive_goals_and_interventions
  list_available_interventions_for_location
  get_interventions
  is_it_outside_work_hours
} = require 'libs_backend/intervention_utils'

{
  get_seconds_spent_on_all_domains_today        # map for all domains
} = require 'libs_common/time_spent_utils'

{
  is_habitlab_enabled
  disable_habitlab
  enable_habitlab
} = require 'libs_common/disable_habitlab_utils'

{
  list_sites_for_which_goals_are_enabled
  list_goals_for_site
  set_goal_enabled
  set_goal_disabled
  add_enable_custom_goal_reduce_time_on_domain
} = require 'libs_backend/goal_utils'

{
  localstorage_getjson
  localstorage_setjson
  localstorage_getbool
  localstorage_setbool
  localstorage_setstring
  localstorage_getstring
} = require 'libs_common/localstorage_utils'

{
  post_json
  get_json
} = require 'libs_backend/ajax_utils'

{
  get_user_id
} = require 'libs_backend/background_common'

{
  once_available
} = require 'libs_frontend/frontend_libs'

polymer_ext {
  is: 'popup-view'
  properties: {
    enabledInterventions: {
      type: Array
    },
    feedbackText: {
      type: String,
      notify: true
    },
    graphOptions: {
      type: Array
    },
    shownGraphs: {
      type: Array
    },
    graphNamesToOptions: {
      type: Object
    },
    blacklist: {
      type: Object
    },
    sites: {
      type: Array
    },
    html_for_shown_graphs: {
      type: String
      computed: 'compute_html_for_shown_graphs(shownGraphs, blacklist, sites)'
    },
    selected_tab_idx: {
      type: Number
      value: 0
    },
    selected_graph_tab: {
      type: Number,
      value: 0
    }
    goals_and_interventions: {
      type: Array
      value: []
    }
    intervention_name_to_info: {
      type: Object
      value: {}
    }
    #url_override: {
    #  type: String
    #}
    is_habitlab_disabled: {
      type: Boolean
    }
    stress_intervention_active: {
      type: Boolean
    }
    stress_intervention_display: {
      type: Boolean
    }
    ask_intervention_done: {
      type: Boolean
    }
    intervention_stress_before: {
      type: Boolean
    }
    intervention_stress_after: {
      type: Boolean
    }
    stress_intervention_data: {
      type: String,
      value: null
    }
    stress_intervention_end: {
      type: String,
      value: null
    }
  }

  get_intervention_description: (intervention_name, intervention_name_to_info) ->
    return intervention_name_to_info[intervention_name].description

  noValidInterventions: ->
    return this.goals_and_interventions.length === 0

  temp_disable_button_clicked: (evt) ->>
    self = this
    intervention = evt.target.intervention
    # <- set_intervention_disabled intervention
    prev_enabled_interventions = await get_enabled_interventions()
    tab_info = await get_active_tab_info()
    url = tab_info.url
    enabledInterventions = await list_currently_loaded_interventions_for_tabid(tab_info.id)
    enabledInterventions = [x for x in enabledInterventions when x != intervention]
    self.enabledInterventions = enabledInterventions
    await disable_interventions_in_active_tab()
    this.fire 'disable_intervention'
    add_log_interventions {
      type: 'intervention_set_temporarily_disabled'
      page: 'popup-view'
      subpage: 'popup-view-active-intervention-tab'
      category: 'intervention_enabledisable'
      now_enabled: false
      is_permanent: false
      manual: true
      url: window.location.href
      tab_url: url
      intervention_name: intervention
      prev_enabled_interventions: prev_enabled_interventions
    }
    #swal({
    #  title: 'Disabled!',
    #  text: 'This intervention will be disabled temporarily.'
    #})

  perm_disable_button_clicked: (evt) ->>
    self = this
    intervention = evt.target.intervention
    prev_enabled_interventions = await get_enabled_interventions()
    await set_intervention_disabled_permanently intervention
    tab_info = await get_active_tab_info()
    url = tab_info.url
    enabledInterventions = await list_currently_loaded_interventions_for_tabid(tab_info.id)
    enabledInterventions = [x for x in enabledInterventions when x != intervention]
    self.enabledInterventions = enabledInterventions
    await disable_interventions_in_active_tab()
    this.fire 'disable_intervention'
    add_log_interventions {
      type: 'intervention_set_permanently_disabled'
      page: 'popup-view'
      subpage: 'popup-view-active-intervention-tab'
      category: 'intervention_enabledisable'
      now_enabled: false
      is_permanent: false
      manual: true
      url: window.location.href
      tab_url: url
      intervention_name: intervention
      prev_enabled_interventions: prev_enabled_interventions
    }
    #swal({
    #  title: 'Disabled!',
    #  text: 'This intervention will be disabled permanently.'
    #})

  is_not_in_blacklist: (graph, blacklist, graphNamesToOptions) ->
    graph = graphNamesToOptions[graph]
    return blacklist[graph] == false

  checkbox_checked_handler: (evt) ->
    self = this
    graph = evt.target.graph
    self.blacklist[self.graphNamesToOptions[graph]] = !evt.target.checked
    self.blacklist = JSON.parse JSON.stringify self.blacklist
    localstorage_setjson('popup_view_graph_blacklist', self.blacklist)

  sortableupdated: (evt) ->
    self = this
    shownGraphs = this.$$('#graphlist_sortable').innerText.split('\n').map((.trim())).filter((x) -> x != '')
    this.shownGraphs = shownGraphs.map((graph_name) -> self.graphNamesToOptions[graph_name])

  compute_html_for_shown_graphs: (shownGraphs, blacklist, sites) ->
    self = this
    shownGraphs = shownGraphs.filter((x) -> !self.blacklist[x])

    html = "
"
    for x in shownGraphs
      if x == 'site-goal-view'
        for site in sites

          html += "<#{x} site=\"#{site}\"></#{x}>
"
      else
        html += "<#{x}></#{x}>
"
    html += "
"
    return html

  isEmpty: (enabledInterventions) ->
    return enabledInterventions? and enabledInterventions.length == 0

  outside_work_hours: ->
    return is_it_outside_work_hours!

  disable_habitlab_changed: (evt) ->>
    if evt.target.checked
      this.is_habitlab_disabled = true
      disable_habitlab()
      tab_info = await get_active_tab_info()
      loaded_interventions = await list_currently_loaded_interventions_for_tabid(tab_info.id)
      add_log_habitlab_disabled({
        page: 'popup-view',
        reason: 'disable_button_slider_toggle'
        tab_info: tab_info
        url: tab_info?url
        loaded_interventions: loaded_interventions
        loaded_intervention: loaded_interventions[0]
      })
    else
      this.is_habitlab_disabled = false
      enable_habitlab()
      tab_info = await get_active_tab_info()
      loaded_interventions = await list_currently_loaded_interventions_for_tabid(tab_info.id)
      add_log_habitlab_enabled({
        page: 'popup-view',
        reason: 'disable_button_slider_toggle'
        tab_info: tab_info
        url: tab_info?url
        loaded_interventions: loaded_interventions
      })

  enable_habitlab_button_clicked: ->>
    this.is_habitlab_disabled = false
    enable_habitlab()
    tab_info = await get_active_tab_info()
    loaded_interventions = await list_currently_loaded_interventions_for_tabid(tab_info.id)
    add_log_habitlab_enabled({
      page: 'popup-view',
      reason: 'enable_habitlab_big_button_clicked'
      tab_info: tab_info
      loaded_interventions: loaded_interventions
    })

  goal_enable_button_changed: (evt) ->>
    goal = evt.target.goal
    if evt.target.checked
      # is enabling this goal
      if goal.name?
        await set_goal_enabled goal.name
      else
        await add_enable_custom_goal_reduce_time_on_domain goal.domain
      await this.set_goals_and_interventions!
    else
      # is disabling this goal
      await set_goal_disabled goal.name
      await this.set_goals_and_interventions!

  set_goals_and_interventions: ->>
    sites_promise = list_sites_for_which_goals_are_enabled()
    enabledInterventions_promise = list_currently_loaded_interventions()
    intervention_name_to_info_promise = get_interventions()
    all_goals_and_interventions_promise = get_nonpositive_goals_and_interventions()
    url_promise = get_active_tab_url()

    [
      sites
      enabledInterventions
      intervention_name_to_info
      all_goals_and_interventions
      url
    ] = await Promise.all [
      sites_promise
      enabledInterventions_promise
      intervention_name_to_info_promise
      all_goals_and_interventions_promise
      url_promise
    ]

    this.sites = sites
    this.enabledInterventions = enabledInterventions
    this.intervention_name_to_info = intervention_name_to_info

    domain = url_to_domain url

    filtered_goals_and_interventions = all_goals_and_interventions.filter (obj) ->

      return (obj.goal.domain == domain) # and obj.enabled

    if filtered_goals_and_interventions.length == 0
      filtered_goals_and_interventions = [
        {
          enabled: false
          goal: {
            domain: domain
            description: "Spend less time on #{domain}"
          }
        }
      ]
    this.goals_and_interventions = filtered_goals_and_interventions

  get_power_icon_src: ->
    return chrome.extension.getURL('icons/power_button.svg')

  get_thumbs_icon_src:->
    return chrome.extension.getURL('icons/thumbs_i')

  debug_button_clicked: ->>
    tab_id = await get_active_tab_id()
    await open_debug_page_for_tab_id(tab_id)

  submit_feedback_clicked: ->>
    #screenshot_utils = await SystemJS.import('libs_common/screenshot_utils')
    screenshot_utils = await get_screenshot_utils()
    screenshot = await screenshot_utils.get_screenshot_as_base64()
    data = await screenshot_utils.get_data_for_feedback()
    feedback_form = document.createElement('feedback-form')
    document.body.appendChild(feedback_form)
    feedback_form.screenshot = screenshot
    feedback_form.other = data
    feedback_form.open()

  help_icon_clicked: ->>
    await load_css_file('bower_components/sweetalert2/dist/sweetalert2.css')
    swal = await get_swal()
    swal {
      title: 'How HabitLab Works'
      html: '''
      HabitLab will help you achieve your goal by showing you a different nudge, like a news feed blocker or a delayed page loader, each time you visit your goal site.

      At first, HabitLab will show you a random nudge each visit, and over time it will learn what works most effectively for you.

      Each visit, HabitLab will test a new nudge and measure how much time you spend on the site. Then it determines the efficacy of each nudge by comparing the time spent per visit when that nudge was deployed, compared to when other nudges are deployed. HabitLab uses an algorithmic technique called multi-armed-bandit to learn which nudges work best and choose which nudges to deploy, to minimize your time wasted online.
      '''
      allowOutsideClick: true
      allowEscapeKey: true
      #showCancelButton: true
      #confirmButtonText: 'Visit Facebook to see an intervention in action'
      #cancelButtonText: 'Close'
    }

############# STRESS INTERVENTION FUNCTIONS

  check_for_survey: ->>
    userid = await get_user_id()
    survey_data = JSON.parse(await get_json(hso_server_url + "/getSurvey", "userid=" + userid))
    if survey_data !== {}
      localstorage_setjson("survey_data", survey_data)
      once_available("survey_button", this.enable_survey_button())

  enable_survey_button: ->>
    survey_data = await localstorage_getjson("survey_data")
    button = document.getElementById("survey_button")
    button.innerHTML = survey_data.button_text
    button.style.display = "flex"
    button.disabled = false

  disable_survey_button: ->>
    localstorage_setjson("survey_data", {})
    button = document.getElementById("survey_button")
    button.style.display = "none"
    button.disabled = true

  survey_button_clicked: ->>
    survey_data = localstorage_getjson("survey_data")
    userid = await get_user_id()
    chrome.tabs.create {url: survey_data.url + '?habitlab_userid=' + userid + '&click_location=dropdown'}
    post_json(hso_server_url + "/surveyClicked", {"_id": survey_data._id, "userid":userid,"click_location":"dropdown"})
    this.disable_survey_button()

  results_button_clicked: ->
    chrome.tabs.create {url: 'options.html#overview'}

  settings_button_clicked: ->
    chrome.tabs.create {url: 'options.html#settings'}

  ready: ->>
    #chrome.browserAction.setBadgeText {text: ''}
    #chrome.browserAction.setBadgeBackgroundColor {color: '#000000'}
    self = this
    is_habitlab_enabled().then (is_enabled) -> self.is_habitlab_disabled = !is_enabled

    #FILTER THIS FOR ONLY THE CURRENT GOAL SITE#
    await this.set_goals_and_interventions!

    have_enabled_custom_interventions = self.enabledInterventions.map(-> self.intervention_name_to_info[it]).filter(-> it?custom).length > 0
    if self.enabledInterventions.length > 0 and (localstorage_getbool('enable_debug_terminal') or have_enabled_custom_interventions)
      self.S('#debugButton').show()

    if self.enabledInterventions.length == 0
      self.selected_tab_idx = 1

    localstorage_setbool('popup_view_has_been_opened', true)

    survey_data = localstorage_getjson("survey_data")

    if survey_data === undefined or survey_data === null
      localstorage_setjson("survey_data",{})
      this.check_for_survey()
    else if survey_data !== {}
      once_available("survey_button", this.enable_survey_button())
    else
      this.check_for_survey()

    setTimeout ->>
      require('../bower_components/iron-icon/iron-icon.deps')
      require('../bower_components/iron-icons/iron-icons.deps')
      require('components/graph-donut-top-sites.deps')
      require('components/intervention-view-single-compact.deps')
      require('components/feedback-form.deps')

      await get_screenshot_utils()
      await get_swal()
    , 1

}, {
  source: require 'libs_frontend/polymer_methods'
  methods: [
    'S'
  ]
}


" Menu Translations:    Simplified Chinese
" Maintainer:           Shun Bai <baishunde@gmail.com>
" Previous Maintainer:  Yuheng Xie <elephant@linux.net.cn>
" Last Change:          2014 Oct 15

" vim: ts=8 sw=8 noet

" Quit when menu translations have already been done.
if exists("did_menu_trans")
  finish
endif
let did_menu_trans = 1
let s:keepcpo= &cpo
set cpo&vim

scriptencoding cp936

" Help menu
menutrans &Help			帮助(&H)
menutrans &Overview<Tab><F1>	纵览(&O)<Tab><F1>
menutrans &User\ Manual		用户手册(&U)
menutrans &How-to\ links	How-to\ 指引(&H)
menutrans &Find\.\.\.		查找(&F)\.\.\.
menutrans &Credits		致谢(&C)
menutrans Co&pying		版权(&P)
menutrans &Sponsor/Register	赞助/注册(&S)
menutrans O&rphans		孤儿(&R)
menutrans &Version		版本(&V)
menutrans &About		关于(&A)

" File menu
menutrans &File				文件(&F)
menutrans &Open\.\.\.<Tab>:e		打开(&O)\.\.\.<Tab>:e
menutrans Sp&lit-Open\.\.\.<Tab>:sp	分割并打开(&L)\.\.\.<Tab>:sp
menutrans Open\ Tab\.\.\.<Tab>:tabnew	打开标签\.\.\.<Tab>:tabnew
menutrans &New<Tab>:enew		新建(&N)<Tab>:enew
menutrans &Close<Tab>:close		关闭(&C)<Tab>:close
menutrans &Save<Tab>:w			保存(&S)<Tab>:w
menutrans Save\ &As\.\.\.<Tab>:sav	另存为(&A)\.\.\.<Tab>:sav
menutrans Split\ &Diff\ with\.\.\.	分割比较(Diff)(&D)\.\.\.
menutrans Split\ Patched\ &By\.\.\.	分割打补丁(Patch)(&B)\.\.\.
menutrans &Print			打印(&P)
menutrans Sa&ve-Exit<Tab>:wqa		保存并退出(&V)<Tab>:wqa
menutrans E&xit<Tab>:qa			退出(&X)<Tab>:qa

" Edit menu
menutrans &Edit				编辑(&E)
menutrans &Undo<Tab>u			撤销(&U)<Tab>u
menutrans &Redo<Tab>^R			重做(&R)<Tab>^R
menutrans Rep&eat<Tab>\.		重复上次操作(&E)<Tab>\.
menutrans Cu&t<Tab>"+x			剪切(&T)<Tab>"+x
menutrans &Copy<Tab>"+y			复制(&C)<Tab>"+y
menutrans &Paste<Tab>"+gP		粘贴(&P)<Tab>"+gP
menutrans Put\ &Before<Tab>[p		粘贴到光标前(&B)<Tab>[p
menutrans Put\ &After<Tab>]p		粘贴到光标后(&A)<Tab>]p
menutrans &Delete<Tab>x			删除(&D)<Tab>x
menutrans &Select\ All<Tab>ggVG		全选(&S)<Tab>ggVG
menutrans &Find\.\.\.			查找(&F)\.\.\.
menutrans Find\ and\ Rep&lace\.\.\.	查找和替换(&L)\.\.\.
menutrans &Find<Tab>/			查找(&F)<Tab>/
menutrans Find\ and\ Rep&lace<Tab>:%s	查找和替换(&L)<Tab>:%s
menutrans Settings\ &Window		设定窗口(&W)
menutrans Startup\ &Settings		启动设定(&S)
menutrans &Global\ Settings		全局设定(&G)

" Edit/Global Settings
menutrans Toggle\ Pattern\ &Highlight<Tab>:set\ hls!	开/关模式高亮(&H)<Tab>:set\ hls!
menutrans Toggle\ &Ignore-case<Tab>:set\ ic!		开/关忽略大小写(&I)<Tab>:set\ ic!
menutrans Toggle\ &Showmatch<Tab>:set\ sm!		开/关显示配对(&S)<Tab>:set\ sm!
menutrans &Context\ lines			上下文行数(&C)

menutrans &Virtual\ Edit			虚拟编辑(&V)
menutrans Never					从不
menutrans Block\ Selection			块选择
menutrans Insert\ mode				插入模式
menutrans Block\ and\ Insert			块选择和插入模式
menutrans Always				总是

menutrans Toggle\ Insert\ &Mode<Tab>:set\ im!	开/关插入模式(&M)<Tab>:set\ im!
menutrans Toggle\ Vi\ C&ompatible<Tab>:set\ cp!	开/关\ Vi\ 兼容<Tab>:set\ cp!
menutrans Search\ &Path\.\.\.			查找路径(&P)\.\.\.
menutrans Ta&g\ Files\.\.\.			Tag\ 文件(&T)\.\.\.

" GUI options
menutrans Toggle\ &Toolbar			开/关工具栏(&T)
menutrans Toggle\ &Bottom\ Scrollbar		开/关底部滚动条(&B)
menutrans Toggle\ &Left\ Scrollbar		开/关左端滚动条(&L)
menutrans Toggle\ &Right\ Scrollbar		开/关右端滚动条(&R)

" Edit/File Settings
menutrans F&ile\ Settings			文件设定(&I)

" Boolean options
menutrans Toggle\ Line\ &Numbering<Tab>:set\ nu!		开/关显示行号(&N)<Tab>:set\ nu!
menutrans Toggle\ relati&ve\ Line\ Numbering<Tab>:set\ rnu!	开/关相对行号(&V)<Tab>:set\ rnu!
menutrans Toggle\ &List\ Mode<Tab>:set\ list!			开/关\ list\ 模式(&L)<Tab>:set\ list!
menutrans Toggle\ Line\ &Wrap<Tab>:set\ wrap!			开/关折行(&W)<Tab>:set\ wrap!
menutrans Toggle\ W&rap\ at\ word<Tab>:set\ lbr!		开/关整词折行(&R)<Tab>:set\ lbr!
menutrans Toggle\ &expand-tab<Tab>:set\ et!			开/关扩展\ tab(&E)<Tab>:set\ et!
menutrans Toggle\ &auto-indent<Tab>:set\ ai!			开/关自动缩进(&A)<Tab>:set\ ai!
menutrans Toggle\ &C-indenting<Tab>:set\ cin!			开/关\ C\ 缩进(&C)<Tab>:set\ cin!

" other options
menutrans &Shiftwidth			缩进宽度(&S)
menutrans Soft\ &Tabstop		Soft\ Tab\ 宽度(&T)
menutrans Te&xt\ Width\.\.\.		文本宽度(&X)\.\.\.
menutrans &File\ Format\.\.\.		文件格式(&F)\.\.\.
menutrans C&olor\ Scheme		配色方案(&O)
menutrans Select\ Fo&nt\.\.\.		选择字体(&N)\.\.\.
menutrans &Keymap			键盘映射(&K)

" Programming menu
menutrans &Tools			工具(&T)
menutrans &Jump\ to\ this\ tag<Tab>g^]	跳转到这个\ tag(&J)<Tab>g^]
menutrans Jump\ &back<Tab>^T		跳转返回(&B)<Tab>^T
menutrans Build\ &Tags\ File		建立\ Tags\ 文件(&T)

" Tools.Spelling Menu
menutrans &Spelling				拼写检查(&S)
menutrans &Spell\ Check\ On			打开拼写检查(&S)
menutrans Spell\ Check\ &Off			关闭拼写检查(&O)
menutrans To\ &Next\ error<Tab>]s		上一个错误(&N)<Tab>]s
menutrans To\ &Previous\ error<Tab>[s		下一个错误(&P)<Tab>[s
menutrans Suggest\ &Corrections<Tab>z=		修正建议(&C)<Tab>z=
menutrans &Repeat\ correction<Tab>:spellrepall	重复修正(&R)<Tab>:spellrepall
menutrans Set\ language\ to\ "en"		设定语言为\ "en"
menutrans Set\ language\ to\ "en_au"		设定语言为\ "en_au"
menutrans Set\ language\ to\ "en_ca"		设定语言为\ "en_ca"
menutrans Set\ language\ to\ "en_gb"		设定语言为\ "en_gb"
menutrans Set\ language\ to\ "en_nz"		设定语言为\ "en_nz"
menutrans Set\ language\ to\ "en_us"		设定语言为\ "en_us"
menutrans &Find\ More\ Languages		查找更多语言(&F)

" Tools.Fold Menu
" open close folds
menutrans &Folding				折叠(&F)
menutrans &Enable/Disable\ folds<Tab>zi		启用/禁用折叠(&E)<Tab>zi
menutrans &View\ Cursor\ Line<Tab>zv		查看此行(&V)<Tab>zv
menutrans Vie&w\ Cursor\ Line\ only<Tab>zMzx	仅查看此行(&W)<Tab>zMzx
menutrans C&lose\ more\ folds<Tab>zm		关闭更多折叠(&L)<Tab>zm
menutrans &Close\ all\ folds<Tab>zM		关闭所有折叠(&C)<Tab>zM
menutrans O&pen\ more\ folds<Tab>zr		打开更多折叠(&P)<Tab>zr
menutrans &Open\ all\ folds<Tab>zR		打开所有折叠(&O)<Tab>zR
" fold method
menutrans Fold\ Met&hod			折叠方法(&H)
menutrans M&anual			手工(&A)
menutrans I&ndent			缩进(&N)
menutrans E&xpression			表达式(&X)
menutrans S&yntax			语法(&Y)
menutrans &Diff				比较(Diff)(&D)
menutrans Ma&rker			标记(&R)
" create and delete folds
menutrans Create\ &Fold<Tab>zf		创建折叠(&F)<Tab>zf
menutrans &Delete\ Fold<Tab>zd		删除折叠(&D)<Tab>zd
menutrans Delete\ &All\ Folds<Tab>zD	删除所有折叠(&A)<Tab>zD
" moving around in folds
menutrans Fold\ column\ &width		折叠栏宽度(&W)

" Tools.Diff Menu
menutrans &Diff				比较(Diff)(&D)
menutrans &Update			更新(&U)
menutrans &Get\ Block			得到块(&G)
menutrans &Put\ Block			放置块(&P)

menutrans &Make<Tab>:make		Make(&M)<Tab>:make
menutrans &List\ Errors<Tab>:cl		列出错误(&L)<Tab>:cl
menutrans L&ist\ Messages<Tab>:cl!	列出消息(&I)<Tab>:cl!
menutrans &Next\ Error<Tab>:cn		下一个错误(&N)<Tab>:cn
menutrans &Previous\ Error<Tab>:cp	上一个错误(&P)<Tab>:cp
menutrans &Older\ List<Tab>:cold	更旧的错误列表(&O)<Tab>:cold
menutrans N&ewer\ List<Tab>:cnew	更新的错误列表(&E)<Tab>:cnew
menutrans Error\ &Window		错误窗口(&W)
menutrans &Update<Tab>:cwin		更新(&U)<Tab>:cwin
menutrans &Open<Tab>:copen		打开(&O)<Tab>:copen
menutrans &Close<Tab>:cclose		关闭(&C)<Tab>:cclose
menutrans &Convert\ to\ HEX<Tab>:%!xxd	转换成十六进制<Tab>:%!xxd
menutrans Conve&rt\ back<Tab>:%!xxd\ -r	转换返回<Tab>:%!xxd\ -r
menutrans Se&T\ Compiler		设定编译器(&T)

" Names for buffer menu.
menutrans &Buffers		缓冲区(&B)
menutrans &Refresh\ menu	更新菜单(&R)
menutrans &Delete		删除(&D)
menutrans &Alternate		交替(&A)
menutrans &Next			下一个(&N)
menutrans &Previous		上一个(&P)

" Window menu
menutrans &Window			窗口(&W)
menutrans &New<Tab>^Wn			新建(&N)<Tab>^Wn
menutrans S&plit<Tab>^Ws		分割(&P)<Tab>^Ws
menutrans Sp&lit\ To\ #<Tab>^W^^	分割到\ #(&L)<Tab>^W^^
menutrans Split\ &Vertically<Tab>^Wv	垂直分割(&V)<Tab>^Wv
menutrans Split\ File\ E&xplorer	分割文件浏览器(&X)
menutrans &Close<Tab>^Wc		关闭(&C)<Tab>^Wc
menutrans Close\ &Other(s)<Tab>^Wo	关闭其它窗口(&O)<Tab>^Wo
menutrans Move\ &To			移动到(&T)
menutrans &Top<Tab>^WK			顶端(&T)<Tab>^WK
menutrans &Bottom<Tab>^WJ		底端(&B)<Tab>^WJ
menutrans &Left\ side<Tab>^WH		左边(&L)<Tab>^WH
menutrans &Right\ side<Tab>^WL		右边(&R)<Tab>^WL
" menutrans Ne&xt<Tab>^Ww		下一个(&X)<Tab>^Ww
" menutrans P&revious<Tab>^WW		上一个(&R)<Tab>^WW
menutrans Rotate\ &Up<Tab>^WR		向上轮换(&U)<Tab>^WR
menutrans Rotate\ &Down<Tab>^Wr		向下轮换(&D)<Tab>^Wr
menutrans &Equal\ Size<Tab>^W=		等大(&E)<Tab>^W=
menutrans &Max\ Height<Tab>^W_		最大高度(&M)<Tab>^W
menutrans M&in\ Height<Tab>^W1_		最小高度(&I)<Tab>^W1_
menutrans Max\ &Width<Tab>^W\|		最大宽度(&W)<Tab>^W\|
menutrans Min\ Widt&h<Tab>^W1\|		最小宽度(&H)<Tab>^W1\|
"
" The popup menu
menutrans &Undo			撤销(&U)
menutrans Cu&t			剪切(&T)
menutrans &Copy			复制(&C)
menutrans &Paste		粘贴(&P)
menutrans &Delete		删除(&D)
menutrans Select\ Blockwise	选择块
menutrans Select\ &Word		选择单词(&W)
menutrans Select\ &Sentence	选择句子(&S)
menutrans Select\ Pa&ragraph	选择段落(&R)
menutrans Select\ &Line		选择行(&L)
menutrans Select\ &Block	选择块(&B)
menutrans Select\ &All		全选(&A)
"
" The GUI toolbar
if has("toolbar")
  if exists("*Do_toolbar_tmenu")
    delfun Do_toolbar_tmenu
  endif
  fun Do_toolbar_tmenu()
    tmenu ToolBar.Open		打开文件
    tmenu ToolBar.Save		保存当前文件
    tmenu ToolBar.SaveAll	保存全部文件
    tmenu ToolBar.Print		打印
    tmenu ToolBar.Undo		撤销
    tmenu ToolBar.Redo		重做
    tmenu ToolBar.Cut		剪切到剪贴板
    tmenu ToolBar.Copy		复制到剪贴板
    tmenu ToolBar.Paste		从剪贴板粘贴
    tmenu ToolBar.Find		查找...
    tmenu ToolBar.FindNext	查找下一个
    tmenu ToolBar.FindPrev	查找上一个
    tmenu ToolBar.Replace	查找和替换...
    tmenu ToolBar.LoadSesn	加载会话
    tmenu ToolBar.SaveSesn	保存当前会话
    tmenu ToolBar.RunScript	运行 Vim 脚本
    tmenu ToolBar.Make		执行 Make (:make)
    tmenu ToolBar.RunCtags	在当前目录建立 tags (!ctags -R .)
    tmenu ToolBar.TagJump	跳转到光标位置的 tag
    tmenu ToolBar.Help		Vim 帮助
    tmenu ToolBar.FindHelp	查找 Vim 帮助
  endfun
endif

" Syntax menu
menutrans &Syntax			语法(&S)
menutrans &Show\ filetypes\ in\ menu	在菜单中显示文件类型(&S)
menutrans &Off				关闭(&O)
menutrans &Manual			手工(&M)
menutrans A&utomatic			自动(&U)
menutrans on/off\ for\ &This\ file	仅对这个文件开/关(&T)
menutrans Co&lor\ test			色彩测试(&L)
menutrans &Highlight\ test		高亮测试(&H)
menutrans &Convert\ to\ HTML		转换成\ HTML(&C)
menutrans Set\ '&syntax'\ only		仅设定\ 'syntax'(&S)
menutrans Set\ '&filetype'\ too		也设定\ 'filetype'(&F)

let &cpo = s:keepcpo
unlet s:keepcpo


/****************************************************************************
 **
 ** Copyright (C) Qxt Foundation. Some rights reserved.
 **
 ** This file is part of the QxtCore module of the Qxt library.
 **
 ** This library is free software; you can redistribute it and/or modify it
 ** under the terms of the Common Public License, version 1.0, as published
 ** by IBM, and/or under the terms of the GNU Lesser General Public License,
 ** version 2.1, as published by the Free Software Foundation.
 **
 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
 ** FITNESS FOR A PARTICULAR PURPOSE.
 **
 ** You should have received a copy of the CPL and the LGPL along with this
 ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
 ** included with the source distribution for more information.
 ** If you did not receive a copy of the licenses, contact the Qxt Foundation.
 **
 ** <http://libqxt.org>  <foundation@libqxt.org>
 **
 ****************************************************************************/

#ifndef QXTGLOBAL_H
#define QXTGLOBAL_H

#include <QtGlobal>

#define QXT_VERSION 0x000600
#define QXT_VERSION_STR "0.6.0"

//--------------------------global macros------------------------------

#ifndef QXT_NO_MACROS

#endif // QXT_NO_MACROS

//--------------------------export macros------------------------------

#define QXT_DLLEXPORT DO_NOT_USE_THIS_ANYMORE

#if !defined(QXT_STATIC)
#    if defined(BUILD_QXT_CORE)
#        define QXT_CORE_EXPORT Q_DECL_EXPORT
#    else
#        define QXT_CORE_EXPORT Q_DECL_IMPORT
#    endif
#else
#    define QXT_CORE_EXPORT
#endif // BUILD_QXT_CORE

#if !defined(QXT_STATIC)
#    if defined(BUILD_QXT_GUI)
#        define QXT_GUI_EXPORT Q_DECL_EXPORT
#    else
#        define QXT_GUI_EXPORT Q_DECL_IMPORT
#    endif
#else
#    define QXT_GUI_EXPORT
#endif // BUILD_QXT_GUI

#if !defined(QXT_STATIC)
#    if defined(BUILD_QXT_NETWORK)
#        define QXT_NETWORK_EXPORT Q_DECL_EXPORT
#    else
#        define QXT_NETWORK_EXPORT Q_DECL_IMPORT
#    endif
#else
#    define QXT_NETWORK_EXPORT
#endif // BUILD_QXT_NETWORK

#if !defined(QXT_STATIC)
#    if defined(BUILD_QXT_SQL)
#        define QXT_SQL_EXPORT Q_DECL_EXPORT
#    else
#        define QXT_SQL_EXPORT Q_DECL_IMPORT
#    endif
#else
#    define QXT_SQL_EXPORT
#endif // BUILD_QXT_SQL

#if !defined(QXT_STATIC)
#    if defined(BUILD_QXT_WEB)
#        define QXT_WEB_EXPORT Q_DECL_EXPORT
#    else
#        define QXT_WEB_EXPORT Q_DECL_IMPORT
#    endif
#else
#    define QXT_WEB_EXPORT
#endif // BUILD_QXT_WEB

#if !defined(QXT_STATIC)
#    if defined(BUILD_QXT_BERKELEY)
#        define QXT_BERKELEY_EXPORT Q_DECL_EXPORT
#    else
#        define QXT_BERKELEY_EXPORT Q_DECL_IMPORT
#    endif
#else
#    define QXT_BERKELEY_EXPORT
#endif // BUILD_QXT_BERKELEY

#if !defined(QXT_STATIC)
#    if defined(BUILD_QXT_ZEROCONF)
#        define QXT_ZEROCONF_EXPORT Q_DECL_EXPORT
#    else
#        define QXT_ZEROCONF_EXPORT Q_DECL_IMPORT
#    endif
#else
#    define QXT_ZEROCONF_EXPORT
#endif // QXT_ZEROCONF_EXPORT

#if defined BUILD_QXT_CORE || defined BUILD_QXT_GUI || defined  BUILD_QXT_SQL || defined BUILD_QXT_NETWORK || defined BUILD_QXT_WEB || defined BUILD_QXT_BERKELEY || defined BUILD_QXT_ZEROCONF
#   define BUILD_QXT
#endif

#ifndef QT_BEGIN_NAMESPACE
#define QT_BEGIN_NAMESPACE
#endif

#ifndef QT_END_NAMESPACE
#define QT_END_NAMESPACE
#endif

#ifndef QT_FORWARD_DECLARE_CLASS
#define QT_FORWARD_DECLARE_CLASS(Class) class Class;
#endif

/****************************************************************************
** This file is derived from code bearing the following notice:
** The sole author of this file, Adam Higerd, has explicitly disclaimed all
** copyright interest and protection for the content within. This file has
** been placed in the public domain according to United States copyright
** statute and case law. In jurisdictions where this public domain dedication
** is not legally recognized, anyone who receives a copy of this file is
** permitted to use, modify, duplicate, and redistribute this file, in whole
** or in part, with no restrictions or conditions. In these jurisdictions,
** this file shall be copyright (C) 2006-2008 by Adam Higerd.
****************************************************************************/

#define QXT_DECLARE_PRIVATE(PUB) friend class PUB##Private; QxtPrivateInterface<PUB, PUB##Private> qxt_d;
#define QXT_DECLARE_PUBLIC(PUB) friend class PUB;
#define QXT_INIT_PRIVATE(PUB) qxt_d.setPublic(this);
#define QXT_D(PUB) PUB##Private& d = qxt_d()
#define QXT_P(PUB) PUB& p = qxt_p()

template <typename PUB>
class QxtPrivate
{
public:
    virtual ~QxtPrivate() = default;
    inline void QXT_setPublic(PUB* pub)
    {
        qxt_p_ptr = pub;
    }

protected:
    inline PUB& qxt_p()
    {
        return *qxt_p_ptr;
    }
    inline const PUB& qxt_p() const
    {
        return *qxt_p_ptr;
    }

private:
    PUB* qxt_p_ptr;
};

template <typename PUB, typename PVT>
class QxtPrivateInterface
{
    friend class QxtPrivate<PUB>;
public:
    QxtPrivateInterface()
    {
        pvt = new PVT;
    }
    ~QxtPrivateInterface()
    {
        delete pvt;
    }

    inline void setPublic(PUB* pub)
    {
        pvt->QXT_setPublic(pub);
    }
    inline PVT& operator()()
    {
        return *static_cast<PVT*>(pvt);
    }
    inline const PVT& operator()() const
    {
        return *static_cast<PVT*>(pvt);
    }
private:
    QxtPrivateInterface(const QxtPrivateInterface&) { }
    QxtPrivateInterface& operator=(const QxtPrivateInterface&) { }
    QxtPrivate<PUB>* pvt;
};

#endif // QXT_GLOBAL


/*
 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include "SVGPathSegList.h"

#include "SVGNames.h"
#include "SVGPathElement.h"
#include "SVGPathUtilities.h"

namespace WebCore {

String SVGPathSegList::valueAsString() const
{
    String pathString;
    buildStringFromSVGPathSegList(*this, pathString, UnalteredParsing);
    return pathString;
}

void SVGPathSegList::commitChange(SVGElement* contextElement, ListModification listModification)
{
    ASSERT(contextElement);
    downcast<SVGPathElement>(contextElement)->pathSegListChanged(m_role, listModification);
}

}


<?php

/*
 * This file is part of the symfony package.
 * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Cache class that stores cached content in XCache.
 *
 * @package    symfony
 * @subpackage cache
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
class sfXCacheCache extends sfCache
{
  /**
   * Initializes this sfCache instance.
   *
   * Available options:
   *
   * * see sfCache for options available for all drivers
   *
   * @see sfCache
   */
  public function initialize($options = array())
  {
    parent::initialize($options);

    if (!function_exists('xcache_set'))
    {
      throw new sfInitializationException('You must have XCache installed and enabled to use sfXCacheCache class.');
    }

    if (!ini_get('xcache.var_size'))
    {
      throw new sfInitializationException('You must set the "xcache.var_size" variable to a value greater than 0 to use sfXCacheCache class.');
    }
  }

 /**
  * @see sfCache
  */
  public function get($key, $default = null)
  {

    $set = $this->getBaseValue($key);

    if (!is_array($set) || !array_key_exists('data', $set))
    {

      return $default;
    }

    return $set['data'];
  }

  /**
   * @see sfCache
   */
  public function has($key)
  {
    return xcache_isset($this->getOption('prefix').$key);
  }

  /**
   * @see sfCache
   */
  public function set($key, $data, $lifetime = null)
  {
    $lifetime = $this->getLifetime($lifetime);

    $set = array(
      'timeout' => time() + $lifetime,
      'data'    => $data,
      'ctime'   => time()
    );

    return xcache_set($this->getOption('prefix').$key, $set, $lifetime);
  }

  /**
   * @see sfCache
   */
  public function remove($key)
  {
    return xcache_unset($this->getOption('prefix').$key);
  }

  /**
   * @see sfCache
   */
  public function clean($mode = sfCache::ALL)
  {
    if ($mode !== sfCache::ALL)
    {
      return true;
    }

    $this->checkAuth();

    for ($i = 0, $max = xcache_count(XC_TYPE_VAR); $i < $max; $i++)
    {
      if (false === xcache_clear_cache(XC_TYPE_VAR, $i))
      {
        return false;
      }
    }

    return true;
  }

  /**
   * @see sfCache
   */
  public function getLastModified($key)
  {
    $set = $this->getBaseValue($key);

    if (!is_array($set) || !array_key_exists('ctime', $set))
    {

      return 0;
    }

    return $set['ctime'];
  }

  /**
   * @see sfCache
   */
  public function getTimeout($key)
  {

    $set = $this->getBaseValue($key);

    if (!is_array($set) || !array_key_exists('timeout', $set))
    {

      return 0;
    }

    return $set['timeout'];
  }

  public function getBaseValue($key)
  {
    return xcache_isset($this->getOption('prefix').$key) ? xcache_get($this->getOption('prefix').$key) : null;
  }

  /**
   * @see sfCache
   */
  public function removePattern($pattern)
  {
    $this->checkAuth();

    $regexp = self::patternToRegexp($this->getOption('prefix').$pattern);

    for ($i = 0, $max = xcache_count(XC_TYPE_VAR); $i < $max; $i++)
    {
      $infos = xcache_list(XC_TYPE_VAR, $i);
      if (!is_array($infos['cache_list']))
      {
        return;
      }

      foreach ($infos['cache_list'] as $info)
      {
        if (preg_match($regexp, $info['name']))
        {
          xcache_unset($info['name']);
        }
      }
    }
  }

  public function getCacheInfo($key)
  {
    $this->checkAuth();

    for ($i = 0, $max = xcache_count(XC_TYPE_VAR); $i < $max; $i++)
    {
      $infos = xcache_list(XC_TYPE_VAR, $i);

      if (is_array($infos['cache_list']))
      {
        foreach ($infos['cache_list'] as $info)
        {
          if ($this->getOption('prefix').$key == $info['name'])
          {
            return $info;
          }
        }
      }
    }

    return null;
  }

  protected function checkAuth()
  {
    if (ini_get('xcache.admin.enable_auth'))
    {
      throw new sfConfigurationException('To use all features of the "sfXCacheCache" class, you must set "xcache.admin.enable_auth" to "Off" in your php.ini.');
    }
  }
}


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.fenix.utils

import android.content.Context
import android.util.AttributeSet
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK
import androidx.appcompat.widget.AppCompatTextView
import org.mozilla.fenix.R

/**
 * An [AppCompatTextView] that announces as link in screen readers for a11y purposes
 */
class LinkTextView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {

    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
        super.onInitializeAccessibilityNodeInfo(info)
        val extras = info?.extras
        extras?.putCharSequence(
            "AccessibilityNodeInfo.roleDescription",
            context.resources.getString(R.string.link_text_view_type_announcement)
        )
        // disable long click  announcement, as there is no action to be performed on long click
        info?.isLongClickable = false
        info?.removeAction(ACTION_LONG_CLICK)
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentTypes.Extensions;
using Orchard.ContentTypes.Services;
using Orchard.ContentTypes.Settings;
using Orchard.ContentTypes.ViewModels;
using Orchard.Core.Contents.Settings;
using Orchard.Environment.Configuration;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Mvc;
using Orchard.UI;
using Orchard.UI.Notify;
using Orchard.Utility.Extensions;

namespace Orchard.ContentTypes.Controllers {
    [ValidateInput(false)]
    public class AdminController : Controller, IUpdateModel {
        private readonly IContentDefinitionService _contentDefinitionService;
        private readonly IContentDefinitionManager _contentDefinitionManager;
        private readonly IPlacementService _placementService;
        private readonly Lazy<IEnumerable<IShellSettingsManagerEventHandler>> _settingsManagerEventHandlers;
        private readonly ShellSettings _settings;

        public AdminController(
            IOrchardServices orchardServices,
            IContentDefinitionService contentDefinitionService,
            IContentDefinitionManager contentDefinitionManager,
            IPlacementService placementService,
            Lazy<IEnumerable<IShellSettingsManagerEventHandler>> settingsManagerEventHandlers,
            ShellSettings settings) {
            Services = orchardServices;
            _contentDefinitionService = contentDefinitionService;
            _contentDefinitionManager = contentDefinitionManager;
            _placementService = placementService;
            _settingsManagerEventHandlers = settingsManagerEventHandlers;
            _settings = settings;

            T = NullLocalizer.Instance;
        }

        public IOrchardServices Services { get; private set; }
        public Localizer T { get; set; }
        public ILogger Logger { get; set; }
        public ActionResult Index() { return List(); }

        #region Types

        public ActionResult List() {
            if (!Services.Authorizer.Authorize(Permissions.ViewContentTypes, T("Not allowed to view content types.")))
                return new HttpUnauthorizedResult();

            return View("List", new ListContentTypesViewModel {
                Types = _contentDefinitionService.GetTypes()
            });
        }

        public ActionResult Create(string suggestion) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to create a content type.")))
                return new HttpUnauthorizedResult();

            return View(new CreateTypeViewModel { DisplayName = suggestion?.Trim(), Name = suggestion?.ToSafeName() });
        }

        [HttpPost, ActionName("Create")]
        public ActionResult CreatePOST(CreateTypeViewModel viewModel) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to create a content type.")))
                return new HttpUnauthorizedResult();

            ValidateDisplayName(viewModel.DisplayName);

            // Additional Display Name validation.
            if (!string.IsNullOrWhiteSpace(viewModel.DisplayName) &&
                _contentDefinitionService.GetTypes().Any(t => string.Equals(t.DisplayName.Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) {
                ModelState.AddModelError("DisplayName", T("A content type with this display name already exists.").Text);
            }

            ValidateTechnicalName(viewModel.Name);

            // Additional Technical Name validation.
            if (!string.IsNullOrWhiteSpace(viewModel.Name) &&
                _contentDefinitionService.GetTypes().Any(t => string.Equals(t.Name.ToSafeName(), viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase))) {
                ModelState.AddModelError("Name", T("A content type with this technical name already exists.").Text);
            }

            if (!ModelState.IsValid) {
                Services.TransactionManager.Cancel();

                return View(viewModel);
            }

            var contentTypeDefinition = _contentDefinitionService.AddType(viewModel.Name, viewModel.DisplayName);

            // CommonPart is added by default to all Content Types.
            _contentDefinitionService.AddPartToType("CommonPart", viewModel.Name);

            var typeViewModel = new EditTypeViewModel(contentTypeDefinition);

            Services.Notifier.Success(T("The \"{0}\" content type has been created.", typeViewModel.DisplayName));

            return RedirectToAction("AddPartsTo", new { id = typeViewModel.Name });
        }

        public ActionResult ContentTypeName(string displayName, int version) {
            return Json(new {
                result = _contentDefinitionService.GenerateContentTypeNameFromDisplayName(displayName),
                version
            });
        }

        public ActionResult FieldName(string partName, string displayName, int version) {
            return Json(new {
                result = _contentDefinitionService.GenerateFieldNameFromDisplayName(partName, displayName),
                version
            });
        }

        public ActionResult Edit(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var typeViewModel = _contentDefinitionService.GetType(id);

            if (typeViewModel == null) return HttpNotFound();

            return View(typeViewModel);
        }

        public ActionResult EditPlacement(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(id);

            if (contentTypeDefinition == null) return HttpNotFound();

            //Grouping Tabs > Cards > Shapes
            var grouped = _placementService
                // Get a collection of objects that describe the placement for all shapes
                // in the editor view for a ContentItem of the given ContentType
                .GetEditorPlacement(id)
                // Order all those shapes based on their position
                .OrderBy(x => x.PlacementInfo.GetPosition(), new FlatPositionComparer())
                // Then alphabetically by their shape type
                .ThenBy(x => x.PlacementSettings.ShapeType)
                // only pick those shapes that live int the "Content" zone
                .Where(e => e.PlacementSettings.Zone == "Content")
                // Form groups whose key is a string like {tabName}%{cardName}. Items
                // in a group represent the shapes that will be in the card called {cardName}
                // in the tab called {tabName}.
                .GroupBy(g => g.PlacementInfo.GetTab() + "%" + g.PlacementInfo.GetCard())
                // Transform each of those groups in an object representing the single cards.
                // Each of these objects contains the name of the tab that contains it, as
                // well as the list of shape placements in that card
                .Select(x =>
                    new Card {
                        Name = x.Key.Split('%')[1],
                        TabName = x.Key.Split('%')[0],
                        Placements = x.ToList()
                    })
                // Group cards by tab
                .GroupBy(x => x.TabName)
                // Since each of those groups "represents" a card, we actually make it into one.
                .Select(x =>
                    new Tab {
                        Name = x.Key,
                        Cards = x.ToList()
                    })
                // Make the collection into a List<Tab> because it's easy to interact with it
                // (see later in the code)
                .ToList();
            var listPlacements = grouped
                // By selecting all placements from the Tab objects we built earlier, we have
                // them ordered nicely
                .SelectMany(x => x.Cards.SelectMany(m => m.Placements))
                .ToList();
            // We want to have an un-named "default" Tab for shapes, in case none was defined
            Tab content;
            if (grouped.Any(x => string.IsNullOrWhiteSpace(x.Name))) {
                // Because of the way the elements of the list have been ordered above,
                // if there is a Tab with empty name, it is the first in the list.
                content = grouped[0];
                grouped.Remove(content);
            } else {
                content = new Tab {
                    Name = "",
                    Cards = new List<Card> { new Card { Name = "", TabName = "", Placements = new List<DriverResultPlacement>() } }
                };
            }
            // In each Tab, we want to have a "default" un-named Card. This will simplfy
            // UI interactions, because it ensures that each Tab has some place we can drop
            // shapes in.
            for (int i = 0; i < grouped.Count(); i++) {
                if (!grouped[i].Cards.Any(x => string.IsNullOrEmpty(x.Name))) {
                    grouped[i].Cards.Insert(0, new Card { Name = "", TabName = grouped[i].Name, Placements = new List<DriverResultPlacement>() });
                }
            }
            var placementModel = new EditPlacementViewModel {
                Content = content,
                AllPlacements = listPlacements,
                Tabs = grouped,
                ContentTypeDefinition = contentTypeDefinition,
            };

            return View(placementModel);
        }

        [HttpPost, ActionName("EditPlacement")]
        [FormValueRequired("submit.Save")]
        public ActionResult EditPlacementPost(string id, EditPlacementViewModel viewModel) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(id);

            if (contentTypeDefinition == null) return HttpNotFound();

            contentTypeDefinition.ResetPlacement(PlacementType.Editor);

            foreach (var placement in viewModel.AllPlacements) {
                var placementSetting = placement.PlacementSettings;

                contentTypeDefinition.Placement(
                    PlacementType.Editor,
                    placementSetting.ShapeType,
                    placementSetting.Differentiator,
                    placementSetting.Zone,
                    placementSetting.Position);
            }

            // Persist placement changes.
            _contentDefinitionManager.StoreTypeDefinition(contentTypeDefinition);

            _settingsManagerEventHandlers.Value.Invoke(x => x.Saved(_settings), Logger);

            return RedirectToAction("EditPlacement", new { id });
        }

        [HttpPost, ActionName("EditPlacement")]
        [FormValueRequired("submit.Restore")]
        public ActionResult EditPlacementRestorePost(string id, EditPlacementViewModel viewModel) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(id);

            if (contentTypeDefinition == null) return HttpNotFound();

            contentTypeDefinition.ResetPlacement(PlacementType.Editor);

            // Persist placement reset.
            _contentDefinitionManager.StoreTypeDefinition(contentTypeDefinition);

            _settingsManagerEventHandlers.Value.Invoke(x => x.Saved(_settings), Logger);

            return RedirectToAction("EditPlacement", new { id });
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("submit.Save")]
        public ActionResult EditPOST(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var typeViewModel = _contentDefinitionService.GetType(id);

            if (typeViewModel == null) return HttpNotFound();

            var edited = new EditTypeViewModel();

            TryUpdateModel(edited);

            ValidateDisplayName(edited.DisplayName);

            // Additional Display Name validation.
            if (!string.IsNullOrWhiteSpace(edited.DisplayName) &&
                _contentDefinitionService.GetTypes().Any(t =>
                    !string.Equals(t.Name, edited.Name, StringComparison.OrdinalIgnoreCase) &&
                    string.Equals(t.DisplayName.Trim(), edited.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) {
                ModelState.AddModelError("DisplayName", T("A content type with this display name already exists.").Text);
            }

            if (!ModelState.IsValid) return View(typeViewModel);

            typeViewModel.DisplayName = edited.DisplayName;

            _contentDefinitionService.AlterType(typeViewModel, this);

            if (!ModelState.IsValid) {
                Services.TransactionManager.Cancel();

                return View(typeViewModel);
            }

            Services.Notifier.Success(T("\"{0}\" settings have been saved.", typeViewModel.DisplayName));

            return RedirectToAction("Edit", new { id });
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("submit.Delete")]
        public ActionResult Delete(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to delete a content type.")))
                return new HttpUnauthorizedResult();

            var typeViewModel = _contentDefinitionService.GetType(id);

            if (typeViewModel == null) return HttpNotFound();

            _contentDefinitionService.RemoveType(id, true);

            Services.Notifier.Success(T("\"{0}\" has been removed.", typeViewModel.DisplayName));

            return RedirectToAction("List");
        }

        public ActionResult AddPartsTo(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var typeViewModel = _contentDefinitionService.GetType(id);

            if (typeViewModel == null) return HttpNotFound();

            var typePartNames = new HashSet<string>(typeViewModel.Parts.Select(tvm => tvm.PartDefinition.Name));

            var viewModel = new AddPartsViewModel {
                Type = typeViewModel,
                PartSelections = _contentDefinitionService.GetParts(metadataPartsOnly: false)
                    .Where(cpd => !typePartNames.Contains(cpd.Name) && cpd.Settings.GetModel<ContentPartSettings>().Attachable)
                    .Select(cpd => new PartSelectionViewModel { PartName = cpd.Name, PartDisplayName = cpd.DisplayName, PartDescription = cpd.Description })
                    .ToList()
            };

            return View(viewModel);
        }

        [HttpPost, ActionName("AddPartsTo")]
        public ActionResult AddPartsToPOST(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var typeViewModel = _contentDefinitionService.GetType(id);

            if (typeViewModel == null) return HttpNotFound();

            var viewModel = new AddPartsViewModel();

            if (!TryUpdateModel(viewModel)) return AddPartsTo(id);

            var partsToAdd = viewModel.PartSelections.Where(ps => ps.IsSelected).Select(ps => ps.PartName);
            foreach (var partToAdd in partsToAdd) {
                _contentDefinitionService.AddPartToType(partToAdd, typeViewModel.Name);

                Services.Notifier.Success(T("The \"{0}\" part has been added.", partToAdd));
            }

            if (!ModelState.IsValid) {
                Services.TransactionManager.Cancel();

                return AddPartsTo(id);
            }

            return RedirectToAction("Edit", new { id });
        }

        public ActionResult RemovePartFrom(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var typeViewModel = _contentDefinitionService.GetType(id);

            var viewModel = new RemovePartViewModel();
            if (typeViewModel == null || !TryUpdateModel(viewModel) ||
                !typeViewModel.Parts.Any(p => p.PartDefinition.Name == viewModel.Name))
                return HttpNotFound();

            viewModel.Type = typeViewModel;

            return View(viewModel);
        }

        [HttpPost, ActionName("RemovePartFrom")]
        public ActionResult RemovePartFromPOST(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var typeViewModel = _contentDefinitionService.GetType(id);

            var viewModel = new RemovePartViewModel();
            if (typeViewModel == null || !TryUpdateModel(viewModel) ||
                !typeViewModel.Parts.Any(p => p.PartDefinition.Name == viewModel.Name))
                return HttpNotFound();

            _contentDefinitionService.RemovePartFromType(viewModel.Name, typeViewModel.Name);

            if (!ModelState.IsValid) {
                Services.TransactionManager.Cancel();
                viewModel.Type = typeViewModel;

                return View(viewModel);
            }

            Services.Notifier.Success(T("The \"{0}\" part has been removed.", viewModel.Name));

            return RedirectToAction("Edit", new { id });
        }

        #endregion

        #region Parts

        public ActionResult ListParts() {
            return View(new ListContentPartsViewModel {
                // only user-defined parts (not code as they are not configurable)
                Parts = _contentDefinitionService.GetParts(metadataPartsOnly: true)
            });
        }

        public ActionResult CreatePart(string suggestion) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to create a content part.")))
                return new HttpUnauthorizedResult();

            return View(new CreatePartViewModel { Name = suggestion?.ToSafeName() });
        }

        [HttpPost, ActionName("CreatePart")]
        public ActionResult CreatePartPOST(CreatePartViewModel viewModel) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to create a content part.")))
                return new HttpUnauthorizedResult();

            ValidateTechnicalName(viewModel.Name);

            // Additional Technical Name validation.
            if (!string.IsNullOrWhiteSpace(viewModel.Name) &&
                _contentDefinitionManager.ListPartDefinitions().Any(t => string.Equals(t.Name.ToSafeName(), viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase))) {
                ModelState.AddModelError("Name", T("A content part with this technical name already exists.").Text);
            }

            if (!ModelState.IsValid) return View(viewModel);

            var partViewModel = _contentDefinitionService.AddPart(viewModel);

            if (partViewModel == null) {
                Services.Notifier.Error(T("The content part could not be created."));

                return View(viewModel);
            }

            Services.Notifier.Success(T("The \"{0}\" content part has been created.", partViewModel.Name));

            return RedirectToAction("EditPart", new { id = partViewModel.Name });
        }

        public ActionResult EditPart(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
                return new HttpUnauthorizedResult();

            var partViewModel = _contentDefinitionService.GetPart(id);

            if (partViewModel == null) return HttpNotFound();

            return View(partViewModel);
        }

        [HttpPost, ActionName("EditPart")]
        [FormValueRequired("submit.Save")]
        public ActionResult EditPartPOST(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
                return new HttpUnauthorizedResult();

            var partViewModel = _contentDefinitionService.GetPart(id);

            if (partViewModel == null) return HttpNotFound();

            if (!TryUpdateModel(partViewModel)) return View(partViewModel);

            _contentDefinitionService.AlterPart(partViewModel, this);

            if (!ModelState.IsValid) {
                Services.TransactionManager.Cancel();

                return View(partViewModel);
            }

            Services.Notifier.Success(T("\"{0}\" settings have been saved.", partViewModel.Name));

            return RedirectToAction("ListParts");
        }

        [HttpPost, ActionName("EditPart")]
        [FormValueRequired("submit.Delete")]
        public ActionResult DeletePart(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to delete a content part.")))
                return new HttpUnauthorizedResult();

            var partViewModel = _contentDefinitionService.GetPart(id);

            if (partViewModel == null) return HttpNotFound();

            _contentDefinitionService.RemovePart(id);

            Services.Notifier.Success(T("\"{0}\" has been removed.", partViewModel.DisplayName));

            return RedirectToAction("ListParts");
        }

        public ActionResult AddFieldTo(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
                return new HttpUnauthorizedResult();

            var partViewModel = _contentDefinitionService.GetPart(id);

            // If the specified Part doesn't exist, try to find a matching Type,
            // where the implicit Part with the same name can be created to store Fields.
            if (partViewModel == null) {
                var typeViewModel = _contentDefinitionService.GetType(id);

                if (typeViewModel == null) return HttpNotFound();
                else partViewModel = new EditPartViewModel(new ContentPartDefinition(id));
            }

            var viewModel = new AddFieldViewModel {
                Part = partViewModel,
                Fields = _contentDefinitionService.GetFields().OrderBy(x => x.FieldTypeName)
            };

            return View(viewModel);
        }

        [HttpPost, ActionName("AddFieldTo")]
        public ActionResult AddFieldToPOST(AddFieldViewModel viewModel, string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
                return new HttpUnauthorizedResult();

            var partViewModel = _contentDefinitionService.GetPart(id);
            var typeViewModel = _contentDefinitionService.GetType(id);

            if (partViewModel == null && typeViewModel == null) return HttpNotFound();

            ValidateDisplayName(viewModel.DisplayName);

            // Additional Display Name validation.
            if (partViewModel != null && !string.IsNullOrWhiteSpace(viewModel.DisplayName) &&
                partViewModel.Fields.Any(t => string.Equals(t.DisplayName.Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) {
                ModelState.AddModelError("DisplayName", T("A content field with this display name already exists.").Text);
            }

            ValidateTechnicalName(viewModel.Name);

            // Additional Technical Name validation.
            if (partViewModel != null && !string.IsNullOrWhiteSpace(viewModel.Name) &&
                partViewModel.Fields.Any(t => string.Equals(t.Name.ToSafeName(), viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase))) {
                ModelState.AddModelError("Name", T("A content field with this technical name already exists.").Text);
            }

            if (!ModelState.IsValid) {
                viewModel.Part = partViewModel ?? new EditPartViewModel { Name = typeViewModel.Name };
                viewModel.Fields = _contentDefinitionService.GetFields();

                Services.TransactionManager.Cancel();

                return View(viewModel);
            }

            // If the specified Part doesn't exist, create an implicit ,
            // where the implicit Part with the same name can be created to store Fields.
            if (partViewModel == null) {
                partViewModel = _contentDefinitionService.AddPart(new CreatePartViewModel { Name = typeViewModel.Name });
                _contentDefinitionService.AddPartToType(partViewModel.Name, typeViewModel.Name);
            }

            try {
                _contentDefinitionService.AddFieldToPart(viewModel.Name, viewModel.DisplayName, viewModel.FieldTypeName, partViewModel.Name);
            }
            catch (Exception ex) {
                Services.Notifier.Error(T("The \"{0}\" field was not added. {1}", viewModel.DisplayName, ex.Message));
                Services.TransactionManager.Cancel();

                return AddFieldTo(id);
            }

            Services.Notifier.Success(T("The \"{0}\" field has been added.", viewModel.DisplayName));

            return typeViewModel == null ? RedirectToAction("EditPart", new { id }) : RedirectToAction("Edit", new { id });
        }

        public ActionResult EditField(string id, string name) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            var partViewModel = _contentDefinitionService.GetPart(id);

            if (partViewModel == null) return HttpNotFound();

            var fieldViewModel = partViewModel.Fields.FirstOrDefault(x => x.Name == name);

            if (fieldViewModel == null) return HttpNotFound();

            var viewModel = new EditFieldNameViewModel {
                Name = fieldViewModel.Name,
                DisplayName = fieldViewModel.DisplayName
            };

            return View(viewModel);
        }

        [HttpPost, ActionName("EditField")]
        [FormValueRequired("submit.Save")]
        public ActionResult EditFieldPOST(string id, EditFieldNameViewModel viewModel) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
                return new HttpUnauthorizedResult();

            if (viewModel == null) return HttpNotFound();

            var partViewModel = _contentDefinitionService.GetPart(id);

            if (partViewModel == null) return HttpNotFound();

            ValidateDisplayName(viewModel.Name);

            // Additional Display Name validation.
            if (!string.IsNullOrWhiteSpace(viewModel.DisplayName) &&
                partViewModel.Fields.Any(f =>
                    !string.Equals(f.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase) &&
                    string.Equals(f.DisplayName.Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) {
                ModelState.AddModelError("DisplayName", T("A content field with this display name already exists on this content part.").Text);
            }

            if (!ModelState.IsValid) return View(viewModel);

            var field = _contentDefinitionManager.GetPartDefinition(id).Fields.FirstOrDefault(x => x.Name == viewModel.Name);

            if (field == null) return HttpNotFound();

            _contentDefinitionService.AlterField(partViewModel, viewModel);

            Services.Notifier.Success(T("Display name changed to {0}.", viewModel.DisplayName));

            // Redirect to the type editor if a type exists with this name.
            return _contentDefinitionService.GetType(id) == null ?
                RedirectToAction("EditPart", new { id }) : RedirectToAction("Edit", new { id });
        }

        public ActionResult RemoveFieldFrom(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
                return new HttpUnauthorizedResult();

            var partViewModel = _contentDefinitionService.GetPart(id);

            var viewModel = new RemoveFieldViewModel();

            if (partViewModel == null || !TryUpdateModel(viewModel) ||
                !partViewModel.Fields.Any(p => p.Name == viewModel.Name))
                return HttpNotFound();

            viewModel.Part = partViewModel;

            return View(viewModel);
        }

        [HttpPost, ActionName("RemoveFieldFrom")]
        public ActionResult RemoveFieldFromPOST(string id) {
            if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
                return new HttpUnauthorizedResult();

            var partViewModel = _contentDefinitionService.GetPart(id);

            var viewModel = new RemoveFieldViewModel();
            if (partViewModel == null || !TryUpdateModel(viewModel) ||
                !partViewModel.Fields.Any(p => p.Name == viewModel.Name))
                return HttpNotFound();

            _contentDefinitionService.RemoveFieldFromPart(viewModel.Name, partViewModel.Name);

            if (!ModelState.IsValid) {
                Services.TransactionManager.Cancel();
                viewModel.Part = partViewModel;

                return View(viewModel);
            }

            Services.Notifier.Success(T("The \"{0}\" field has been removed.", viewModel.Name));

            // Redirect to the type editor if a type exists with this name.
            return _contentDefinitionService.GetType(id) == null ?
                RedirectToAction("EditPart", new { id }) : RedirectToAction("Edit", new { id });
        }

        #endregion

        private void ValidateDisplayName(string displayName) {
            if (string.IsNullOrWhiteSpace(displayName)) {
                ModelState.AddModelError("DisplayName", T("The display name name can't be empty.").Text);
            }
            else if (!string.Equals(displayName, displayName.Trim(), StringComparison.OrdinalIgnoreCase)) {
                ModelState.AddModelError("DisplayName", T("The display name starts and/or ends with whitespace characters.").Text);
            }
        }

        private void ValidateTechnicalName(string technicalName) {
            if (string.IsNullOrWhiteSpace(technicalName)) {
                ModelState.AddModelError("Name", T("The technical name (Id) can't be empty.").Text);
            }
            else {
                var safeTechnicalName = technicalName.ToSafeName();

                if (!string.Equals(technicalName, safeTechnicalName, StringComparison.OrdinalIgnoreCase)) {
                    ModelState.AddModelError("Name", T("The technical name contains invalid (non-alphanumeric) characters.").Text);
                }

                if (!safeTechnicalName.FirstOrDefault().IsLetter()) {
                    ModelState.AddModelError("Name", T("The technical name must start with a letter.").Text);
                }
            }
        }

        bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
            return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
        }

        void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
            ModelState.AddModelError(key, errorMessage.ToString());
        }
    }
}


package com.open.capacity.server.oauth2;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.open.capacity.server.oauth2.client.RedisClientDetailsService;
import com.open.capacity.server.oauth2.code.RedisAuthorizationCodeServices;
import com.open.capacity.server.oauth2.token.store.RedisTemplateTokenStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @author owen 624191343@qq.com
 * @version 创建时间：2017年11月12日 上午22:57:51
 */
@Configuration

public class OAuth2ServerConfig {

    private Logger logger = LoggerFactory.getLogger(OAuth2ServerConfig.class);

    @Resource
    private DataSource dataSource;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Bean // 声明 ClientDetails实现
    @ConditionalOnProperty(prefix = "security.oauth2.token.store", name = "type", havingValue = "redis", matchIfMissing = true)
    public RedisClientDetailsService clientDetailsService() {
        RedisClientDetailsService clientDetailsService = new RedisClientDetailsService(dataSource);
        clientDetailsService.setRedisTemplate(redisTemplate);
        return clientDetailsService;
    }

//	@Bean
//    public ApprovalStore approvalStore() {
//        return new JdbcApprovalStore(dataSource);
//    }

    @Bean
    public RandomValueAuthorizationCodeServices authorizationCodeServices() {
        RedisAuthorizationCodeServices redisAuthorizationCodeServices = new RedisAuthorizationCodeServices();
        redisAuthorizationCodeServices.setRedisTemplate(redisTemplate);
        return redisAuthorizationCodeServices;
    }

    /**
     * @author owen 624191343@qq.com
     * @version 创建时间：2017年11月12日 上午22:57:51
     * 默认token存储在内存中
     * DefaultTokenServices默认处理
     */
    @Component
    @Configuration
    @EnableAuthorizationServer
    @AutoConfigureAfter(AuthorizationServerEndpointsConfigurer.class)
    public class UnieapAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
        /**
         * 注入authenticationManager 来支持 password grant type
         */
        @Autowired
        private AuthenticationManager authenticationManager;

        @Resource
        private ObjectMapper objectMapper; // springmvc启动时自动装配json处理类

        @Autowired
        private UserDetailsService userDetailsService;
        @Autowired(required = false)
        private RedisTemplateTokenStore redisTokenStore;

        @Autowired(required = false)
        private JwtTokenStore jwtTokenStore;
        @Autowired(required = false)
        private JwtAccessTokenConverter jwtAccessTokenConverter;

        @Autowired
        private WebResponseExceptionTranslator webResponseExceptionTranslator;

        @Autowired
        private RedisClientDetailsService clientDetailsService;

        @Autowired(required = false)
        private RandomValueAuthorizationCodeServices authorizationCodeServices;

        //配置身份认证器，配置认证方式，TokenStore，TokenGranter，OAuth2RequestFactory
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

            if (jwtTokenStore != null) {
                endpoints.tokenStore(jwtTokenStore).authenticationManager(authenticationManager)
                        .userDetailsService(userDetailsService); // 支持
                // password
                // grant
                // type;
            } else if (redisTokenStore != null) {
                endpoints.tokenStore(redisTokenStore).authenticationManager(authenticationManager)
                        .userDetailsService(userDetailsService); // 支持
                // password
                // grant
                // type;
            }

            if (jwtAccessTokenConverter != null) {
                endpoints.accessTokenConverter(jwtAccessTokenConverter);
            }

            endpoints.authorizationCodeServices(authorizationCodeServices);

            endpoints.exceptionTranslator(webResponseExceptionTranslator);

        }

        // 配置应用名称 应用id
        //配置OAuth2的客户端相关信息
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

            // if(clientDetailsService!=null){
            // clients.withClientDetails(clientDetailsService);
            // }else{
            // clients.inMemory().withClient("neusoft1").secret("neusoft1")
            // .authorizedGrantTypes("authorization_code", "password",
            // "refresh_token").scopes("all")
            // .resourceIds(SERVER_RESOURCE_ID).accessTokenValiditySeconds(1200)
            // .refreshTokenValiditySeconds(50000)
            // .and().withClient("neusoft2").secret("neusoft2")
            // .authorizedGrantTypes("authorization_code", "password",
            // "refresh_token").scopes("all")
            // .resourceIds(SERVER_RESOURCE_ID).accessTokenValiditySeconds(1200)
            // .refreshTokenValiditySeconds(50000)
            // ;
            // }
            clients.withClientDetails(clientDetailsService);
            clientDetailsService.loadAllClientToCache();
        }

        //对应于配置AuthorizationServer安全认证的相关信息，创建ClientCredentialsTokenEndpointFilter核心过滤器
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

            security.tokenKeyAccess("permitAll()") /// url:/oauth/token_key,exposes
                    /// public key for token
                    /// verification if using
                    /// JWT tokens
                    .checkTokenAccess("isAuthenticated()") // url:/oauth/check_token
                    // allow check token
                    .allowFormAuthenticationForClients();

            // security.allowFormAuthenticationForClients();
            //// security.tokenKeyAccess("permitAll()");
            // security.tokenKeyAccess("isAuthenticated()");
        }

    }

    // add for sso
    // 在ResourceServerConfigurerAdapter配置需要token验证的资源
    @Configuration
    @EnableResourceServer
    public class ResourceServer extends ResourceServerConfigurerAdapter {

        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/health");
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            // http.httpBasic() //默认配置
            // 用表单登录
            http.formLogin()
                    // 对请求授权
                    .and().authorizeRequests()
                    .antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**").permitAll()
                    // 所有需要restful保护的资源都需要加入到这个requestMatchers，加入到的资源作为资源服务器保护的资源
                    .and().requestMatchers()
                    .antMatchers("/users", "/**/users").and().authorizeRequests()
                    .antMatchers("/**/users", "/users").authenticated().anyRequest().authenticated() // 所有的请求认证
                    .and().csrf().disable() // 关闭Could not verify the provided
            // CSRF
            // token because your session was
            // not
            // found
            ;
        }
    }

}


package Spacecraft.Ctors
  with SPARK_Mode => On
is
   function Satellite_Params_Valid(Time: DKTime; Revolutions: DKFloat; Altitude: DKFloat) return Boolean;
   function New_Satellite(Time: DKTime; Revolutions: DKFloat; Altitude: DKFloat) return Satellite
     with
       Pre => (Satellite_Params_Valid(Time, Revolutions, Altitude));
private
   function Satellite_Params_Valid(Time: DKTime; Revolutions: DKFloat; Altitude: DKFloat) return Boolean is
      (Revolutions * 2.0 * Pi * Altitude < DKFloat'Last and then (Revolutions * 2.0 * Pi * Altitude) / Time < DKFloat'Last);
end Spacecraft.Ctors;


//
//  Rosetta Stone
//  http://product.rosettastone.com/news/
//
//
//  Documentation
//  http://cocoadocs.org/docsets/RSTCoreDataKit
//
//
//  GitHub
//  https://github.com/rosettastone/RSTCoreDataKit
//
//
//  License
//  Copyright (c) 2014 Rosetta Stone
//  Released under a BSD license: http://opensource.org/licenses/BSD-3-Clause
//

#import "RSTCoreDataKitTestCase.h"

@interface RSTCoreDataContextDidSaveListenerTests : RSTCoreDataKitTestCase

@end

@implementation RSTCoreDataContextDidSaveListenerTests

- (void)testContextDidSaveListener
{
    XCTestExpectation *expection = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __PRETTY_FUNCTION__]];

    // GIVEN: a new context save listener
    RSTCoreDataContextDidSaveListener *listener = [[RSTCoreDataContextDidSaveListener alloc] initWithHandler:^(NSNotification *notification) {
        XCTAssertEqualObjects(notification.name, NSManagedObjectContextDidSaveNotification, @"Should receive expected notification");
        [expection fulfill];
    } forManagedObjectContext:nil];

    XCTAssertNotNil(listener);

    // WHEN: the context did save notification is posted
    [[NSNotificationCenter defaultCenter] postNotificationName:NSManagedObjectContextDidSaveNotification object:nil];

    // THEN: our handler block is called without an error
    [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
        XCTAssertNil(error, @"Expectation should not error");
    }];
}

- (void)testContextDidSaveListenerForSpecificContext
{
    XCTestExpectation *expection = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __PRETTY_FUNCTION__]];

    // GIVEN: a new context save listener and context
    NSManagedObjectContext *context = self.testStack.managedObjectContext;

    RSTCoreDataContextDidSaveListener *listener = [[RSTCoreDataContextDidSaveListener alloc] initWithHandler:^(NSNotification *notification) {
        XCTAssertEqualObjects(notification.name, NSManagedObjectContextDidSaveNotification, @"Should receive expected notification");
        [expection fulfill];
    } forManagedObjectContext:context];

    XCTAssertNotNil(listener);

    // WHEN: the specified context is changed and saved
    [self insertFakeEmployee];

    [RSTCoreDataContextSaver saveAndWait:context];

    // THEN: our handler block is called without an error
    [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
        XCTAssertNil(error, @"Expectation should not error");
    }];
}

- (void)testContextDidSaveListenerForOtherNonregisteredContext
{
    // GIVEN: a new context save listener and 2 different contexts
    NSManagedObjectContext *mainContext = self.testStack.managedObjectContext;
    NSManagedObjectContext *childContext = [self.testStack newDefaultMainChildContext];

    __block BOOL handlerCalled = NO;

    RSTCoreDataContextDidSaveListener *listenerForChildContext = [[RSTCoreDataContextDidSaveListener alloc] initWithHandler:^(NSNotification *notification) {
        handlerCalled = YES;
    } forManagedObjectContext:childContext];

    XCTAssertNotNil(listenerForChildContext);

    // WHEN: a context is changed and saved that is NOT registered with the listener
    [self insertFakeEmployee];

    [RSTCoreDataContextSaver saveAndWait:mainContext];

    // THEN: our handler block is NOT called
    XCTAssertFalse(handlerCalled);
}

@end