<commit_before>const fs = require('fs');
const path = require('path');
const request = require('request-promise-native');

const TRANSLATED_MIN_PROGRESS = 0.95;
const CONTRIBUTABLE_MIN_SENTENCES = 1000;

const dataPath = path.join(__dirname, '..', 'locales');

function saveDataJSON(name, data) {
  fs.writeFileSync(
    path.join(dataPath, name + '.json'),
    JSON.stringify(data, null, 2)
  );
}

async function fetchPontoonLanguages() {
  const { data } = await request({
    uri: 'https://pontoon.mozilla.org/graphql',
    method: 'POST',
    json: true,
    body: {
      query: `{
            project(slug: "common-voice") {
              localizations {
                totalStrings
                approvedStrings
                locale {
                  code
                  name
                }
              }
            }
          }`,
      variables: null,
    },
  });
  return data.project.localizations
    .map(({ totalStrings, approvedStrings, locale }) => [
      locale.code,
      locale.name,
      approvedStrings / totalStrings,
    ])
    .concat([['en', 'English', 1]])
    .sort(([code1], [code2]) => code1.localeCompare(code2));
}

async function saveToMessages(languages) {
  const messagesPath = path.join(
    __dirname,
    '..',
    'web',
    'locales',
    'en',
    'messages.ftl'
  );
  const messages = fs.readFileSync(messagesPath, 'utf-8');
  const newMessages = messages.replace(
    /#\s\[Languages]([\s\S]*?)#\s\[\/]/gm,
    [
      '# [Languages]',
      '## Languages',
      languages.map(([code, name]) => `${code} = ${name}`).join('\n'),
      '# [/]',
    ].join('\n')
  );
  fs.writeFileSync(messagesPath, newMessages);
}

/**
 * Saves completed locales (always merges with previously added locales)
 */
async function saveCompletedLocalesJSON(languages) {
  const fileName = 'translated';
  const completedLocalesPath = path.join(dataPath, fileName + '.json');
  const existingLocales = JSON.parse(
    fs.readFileSync(completedLocalesPath, 'utf-8')
  );
  saveDataJSON(
    fileName,
    [
      ...new Set([
        ...existingLocales,
        ...languages
          .filter(
            ([code, name, progress]) => progress >= TRANSLATED_MIN_PROGRESS
          )
          .map(l => l[0]),
      ]),
    ].sort()
  );
}

async function importPontoonLocales() {
  const languages = await fetchPontoonLanguages();
  await Promise.all([
    saveToMessages(languages),
    saveDataJSON(
      'all',
      languages.reduce((obj, [k, v]) => {
        obj[k] = v;
        return obj;
      }, {})
    ),
    saveCompletedLocalesJSON(languages),
  ]);
}

async function importContributableLocales() {
  const sentencesPath = path.join(__dirname, '..', 'server', 'data');
  const names = fs.readdirSync(sentencesPath).filter(name => {
    if (name === 'LICENSE') {
      return false;
    }
    const localeSentencesPath = path.join(sentencesPath, name);
    const count = fs
      .readdirSync(localeSentencesPath)
      .reduce(
        (count, sentencesFile) =>
          sentencesFile.endsWith('.txt')
            ? count +
              fs
                .readFileSync(
                  path.join(localeSentencesPath, sentencesFile),
                  'utf-8'
                )
                .split('\n').length
            : count,
        0
      );
    return count > CONTRIBUTABLE_MIN_SENTENCES;
  });
  saveDataJSON('contributable', names.sort(), null, 2);
}

async function importLocales() {
  await Promise.all([/*importPontoonLocales(),*/ importContributableLocales()]);
}

importLocales().catch(e => console.error(e));
<commit_msg>uncomment pontoon locale import<commit_after>const fs = require('fs');
const path = require('path');
const request = require('request-promise-native');

const TRANSLATED_MIN_PROGRESS = 0.95;
const CONTRIBUTABLE_MIN_SENTENCES = 1000;

const dataPath = path.join(__dirname, '..', 'locales');

function saveDataJSON(name, data) {
  fs.writeFileSync(
    path.join(dataPath, name + '.json'),
    JSON.stringify(data, null, 2)
  );
}

async function fetchPontoonLanguages() {
  const { data } = await request({
    uri: 'https://pontoon.mozilla.org/graphql',
    method: 'POST',
    json: true,
    body: {
      query: `{
            project(slug: "common-voice") {
              localizations {
                totalStrings
                approvedStrings
                locale {
                  code
                  name
                }
              }
            }
          }`,
      variables: null,
    },
  });
  return data.project.localizations
    .map(({ totalStrings, approvedStrings, locale }) => [
      locale.code,
      locale.name,
      approvedStrings / totalStrings,
    ])
    .concat([['en', 'English', 1]])
    .sort(([code1], [code2]) => code1.localeCompare(code2));
}

async function saveToMessages(languages) {
  const messagesPath = path.join(
    __dirname,
    '..',
    'web',
    'locales',
    'en',
    'messages.ftl'
  );
  const messages = fs.readFileSync(messagesPath, 'utf-8');
  const newMessages = messages.replace(
    /#\s\[Languages]([\s\S]*?)#\s\[\/]/gm,
    [
      '# [Languages]',
      '## Languages',
      languages.map(([code, name]) => `${code} = ${name}`).join('\n'),
      '# [/]',
    ].join('\n')
  );
  fs.writeFileSync(messagesPath, newMessages);
}

/**
 * Saves completed locales (always merges with previously added locales)
 */
async function saveCompletedLocalesJSON(languages) {
  const fileName = 'translated';
  const completedLocalesPath = path.join(dataPath, fileName + '.json');
  const existingLocales = JSON.parse(
    fs.readFileSync(completedLocalesPath, 'utf-8')
  );
  saveDataJSON(
    fileName,
    [
      ...new Set([
        ...existingLocales,
        ...languages
          .filter(
            ([code, name, progress]) => progress >= TRANSLATED_MIN_PROGRESS
          )
          .map(l => l[0]),
      ]),
    ].sort()
  );
}

async function importPontoonLocales() {
  const languages = await fetchPontoonLanguages();
  await Promise.all([
    saveToMessages(languages),
    saveDataJSON(
      'all',
      languages.reduce((obj, [k, v]) => {
        obj[k] = v;
        return obj;
      }, {})
    ),
    saveCompletedLocalesJSON(languages),
  ]);
}

async function importContributableLocales() {
  const sentencesPath = path.join(__dirname, '..', 'server', 'data');
  const names = fs.readdirSync(sentencesPath).filter(name => {
    if (name === 'LICENSE') {
      return false;
    }
    const localeSentencesPath = path.join(sentencesPath, name);
    const count = fs
      .readdirSync(localeSentencesPath)
      .reduce(
        (count, sentencesFile) =>
          sentencesFile.endsWith('.txt')
            ? count +
              fs
                .readFileSync(
                  path.join(localeSentencesPath, sentencesFile),
                  'utf-8'
                )
                .split('\n').length
            : count,
        0
      );
    return count > CONTRIBUTABLE_MIN_SENTENCES;
  });
  saveDataJSON('contributable', names.sort(), null, 2);
}

async function importLocales() {
  await Promise.all([importPontoonLocales(), importContributableLocales()]);
}

importLocales().catch(e => console.error(e));
<|endoftext|>


<commit_before>'use strict';

export default class extends think.controller.base {

  /**
   * 前置操作
   */
  async __before() {

    //根据token值获取用户id
    think.token = this.header('X-Weshow-Token') || '';
    let TokenSerivce = this.service('token');
    let tokenObj = new TokenSerivce();
    think.userId = await tokenObj.getUserId();

    const publicController = this.http.config('publicController');
    const publicAction = this.http.config('publicAction');

    //如果为非公开，则验证用户是否登录
    console.log(this.http.controller + '/' + this.http.action)
    if (!publicController.includes(this.http.controller) && !publicAction.includes(this.http.controller + '/' + this.http.action)) {
      if (think.userId <= 0) {
        return this.fail(401, '请先登录');
      }
    }

  }

  /**
   * 根据时间格式化显示
   * @param time
   * @returns {string}
   */
  formatDateTime(time){
    let cur = new Date();
    let date = new Date(time * 1000);
    let out = "";
    let curTime = cur.getTime() / 1000;

    if (curTime - time < 3600) {
      out = Math.round((curTime - time) / 60) + '分钟前';
    }
    else if (curTime - time < 3600 * 24) {
      out = Math.round((curTime - time) / 3600) + '小时前';
    }
    else if (curTime - time < 3600 * 24 * 7) {
      out = (cur.getDay() - date.getDay()) + '天前';
    }
    else if (cur.getYear() == date.getYear()) {
      out = date.getMonth() + '月' + (date.getDay()+1) + '日';
    }
    else {
      out = date.getFullYear() + '/' + date.getMonth() + '/' + (date.getDay()+1);
    }
    return out;
  }

  function formatNumber(n) {
    n = n.toString();
    return n[1] ? n : '0' + n;
  }

  getFullDateTime(time) {
    let date = new Date(time * 1000);
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();

    var hour = date.getHours();
    var minute = date.getMinutes();
    var second = date.getSeconds();

    return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':');
  }
}<commit_msg>Format<commit_after>'use strict';

export default class extends think.controller.base {

  /**
   * 前置操作
   */
  async __before() {

    //根据token值获取用户id
    think.token = this.header('X-Weshow-Token') || '';
    let TokenSerivce = this.service('token');
    let tokenObj = new TokenSerivce();
    think.userId = await tokenObj.getUserId();

    const publicController = this.http.config('publicController');
    const publicAction = this.http.config('publicAction');

    //如果为非公开，则验证用户是否登录
    console.log(this.http.controller + '/' + this.http.action)
    if (!publicController.includes(this.http.controller) && !publicAction.includes(this.http.controller + '/' + this.http.action)) {
      if (think.userId <= 0) {
        return this.fail(401, '请先登录');
      }
    }

  }

  /**
   * 根据时间格式化显示
   * @param time
   * @returns {string}
   */
  formatDateTime(time){
    let cur = new Date();
    let date = new Date(time * 1000);
    let out = "";
    let curTime = cur.getTime() / 1000;

    if (curTime - time < 3600) {
      out = Math.round((curTime - time) / 60) + '分钟前';
    }
    else if (curTime - time < 3600 * 24) {
      out = Math.round((curTime - time) / 3600) + '小时前';
    }
    else if (curTime - time < 3600 * 24 * 7) {
      out = (cur.getDay() - date.getDay()) + '天前';
    }
    else if (cur.getYear() == date.getYear()) {
      out = date.getMonth() + '月' + (date.getDay()+1) + '日';
    }
    else {
      out = date.getFullYear() + '/' + date.getMonth() + '/' + (date.getDay()+1);
    }
    return out;
  }

  formatNumber(n) {
    n = n.toString();
    return n[1] ? n : '0' + n;
  }

  getFullDateTime(time) {
    let date = new Date(time * 1000);
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();

    var hour = date.getHours();
    var minute = date.getMinutes();
    var second = date.getSeconds();

    //return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':');
	return year + '-' + month + '-' + day + '-' + formatNumber(hour) + '-' + formatNumber(minute) + '-' + formatNumber(second);
  }
}<|endoftext|>


<commit_before>/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.

 * ***************************************************************************

    In this file we extend the standard Blockly library with application-specific functionality
    that seems to be core enough to become core extension.
 */

/**
 * This file contains methods that ensure that the blocks in the Blockly workspace are attached to each other in a valid sequence.
 */

function validateBlocklyGraph(){
	var workspace = Blockly.getMainWorkspace();
    var blocks = workspace.getTopBlocks();
	var allowSaving = true;
    if(blocks.length == 0){
        alert("Workspace is empty");
		return;
    } else if(blocks.length > 1){
		alert("Only the start block is allowed to be placed directly in the workspace");
		return;
	} else{
		var startBlock = blocks[0];
		allowSaving = validateBlock(startBlock);
	}
	if(allowSaving){
		alert("You may save this");
	}
}

//get all blocks. Send each block's slots for validation. Send each child block of each slot for validation
function validateBlock(block){
	var validationResult = true;
	var notchNumbers = notchToBlockMapper[block.type];
	if(notchNumbers){
		for(var i=notchNumbers[0] ; i<notchNumbers[1] ; i++){
			var notch = block.getInput(''+i);
			var slotContentsList = block.getSlotContentsList(notch.name);
			var notchValidationBoolean = validateNotch(notch, i , slotContentsList);
			if( !notchValidationBoolean ){
				validationResult = false;
			}
			for(var j=0;j<slotContentsList.length;j++){
				var blockValidationBoolean = validateBlock(slotContentsList[j]);
				if( !blockValidationBoolean ){
					validationResult = false;
				}
			}
		}
	}
	return validationResult;
}

function validateNotch(notch, notchNumber , slotContents){
	var allClear = true;
	//var notch = block.getInput(''+notchNumber)
	if(notch.connection.targetBlock() == null){
		if( notchProperties[notchNumber].canBeEmpty == false ){
			alert("slot " + notchNumber + " needs to have something in it");
			allClear = false;
		}
	} else{
		if(notchProperties[notchNumber].isRepeatable){
			if(notchProperties[notchNumber].isGrouped){   // one/zeroOrMore has interleave as only child

			} else{                             // one/zeroOrMore has choice, optional as only child

			}
		} else{
			if(notchProperties[notchNumber].isGrouped){    // interleave notch notch

			} else{                              //optional, choice notch
				allClear = validateChoiceNotch(notch.name, slotContents);
			}
		}
	}
	return allClear;
}

function validateChoiceNotch(notchName, slotContents){
	var expectedChildren = JSON.parse( JSON.stringify( notchProperties[notchName].childrenInfo ) );
    var actualChildren = getPrettyNamesOfSlotContents(slotContents);
	if( actualChildren.length == 1 ){
        if( expectedChildren.indexOf( actualChildren[0] ) != -1 ){  //if child is directly mentioned in expectedChildren, it is not an interleave
			return true;
        }
    }

    if( isRepetitiveChild( expectedChildren , actualChildren[0]) ){ //choice has a oneOrMore child
        var iterator = 1;
        while( iterator < actualChildren.length ){
            if( actualChildren[0] == actualChildren[iterator] ){
                iterator++;
                continue;
            } else{
                alert("Please attach only one type of repetitive block");
                return false;
            }
        }
    } else{
        var interleaveLists = isInterleaveChild(expectedChildren , actualChildren[0]);
        if(interleaveLists.length>0){   //choice has an interleave child
            for(var i=0;i<interleaveLists.length;i++){
                var currentList = JSON.parse(interleaveLists[i]);
                var ans = false;
                if(currentList.length != actualChildren.length){
                    /*if we have two interleaves in the choice [a,b] and [a,b,c],
                     *then we proceed ahead only for that list which has the same number of elements as the current notch
                     */
                     ans = false;
                    continue;
                }
                ans = true;
                while(currentList.length!=0){   //we may be missing interleave containing a repetitive block
                    if( actualChildren.indexOf(currentList[0]) != -1 ){
                        currentList.splice(0,1);
                    } else{
                        ans = false;
                        break;
                    }
                }
                if(ans == true){
                    return true;
                }
            }
            alert("The interleave has not been implemented properly.");
            return false;
        } else{ //if block occurs multiple times but is neither a repetitive nor interleave block
			alert("You cannot attach this block more than once");
			return false;
		}
    }
	return true;
}

function isRepetitiveChild(expectedChildren , name){
    var ans = false;
    var index = expectedChildren.indexOf(name);
    var i = index-1;
    while(i>=0){
        if( expectedChildren[i] == "startRepetition_" ){
            break;
        }
        i--;
    }
    if(i>=0){
        while(i<expectedChildren.length){
            if( expectedChildren[i] == "_endRepetition" ){
                break;
            }
            i++;
        }
        if(i>index && i!=expectedChildren.length){
            ans = true;
        }
    }
    return ans;
}

function isInterleaveChild( expectedChildren , name ){
    var listsThatChildCanBePartOf = [];
    for(var i=0;i<expectedChildren.length;i++){
        if( Object.prototype.toString.call ( expectedChildren[i] ) === "[object Array]" ){
            if(expectedChildren[i].indexOf(name) != -1 ){
                listsThatChildCanBePartOf.push(JSON.stringify(expectedChildren[i]));
            }
        }
    }
    return listsThatChildCanBePartOf;

}

function getPrettyNamesOfSlotContents(blockArray){
	var ans = [];
	for(var i=0; i<blockArray.length; i++){
		ans.push( blockNameToDisplayNameMapper[blockArray[i].type] );
	}
	return ans;
}
<commit_msg>interface simplification: no need to pass around notch objects or deal directly with connections<commit_after>/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.

 * ***************************************************************************

    In this file we extend the standard Blockly library with application-specific functionality
    that seems to be core enough to become core extension.
 */

/**
 * This file contains methods that ensure that the blocks in the Blockly workspace are attached to each other in a valid sequence.
 */

function validateBlocklyGraph(){
    var blocks = Blockly.getMainWorkspace().getTopBlocks();

    if(blocks.length == 0){
        alert("Workspace is empty");
        return false;
    } else if(blocks.length > 1){
		alert("Only the start block is allowed to be placed directly in the workspace");
        return false;
	} else {
        var blocklyValidationResult = validateBlock(blocks[0]);

        if(blocklyValidationResult) {
            alert("You may save this");
        }
        return blocklyValidationResult;
    }
}

//get all blocks. Send each block's slots for validation. Send each child block of each slot for validation
function validateBlock(block){
	var blockValidationResult = true;

	var notchNumbers = notchToBlockMapper[block.type];
	if(notchNumbers){
		for(var i=notchNumbers[0] ; i<notchNumbers[1] ; i++){
			var slotContentsList = block.getSlotContentsList(''+i);

			blockValidationResult = blockValidationResult && validateNotch(i , slotContentsList); // check if the notch is correctly populated

			for(var j=0;j<slotContentsList.length;j++){
				blockValidationResult = blockValidationResult && validateBlock(slotContentsList[j]); // check each of the child blocks in turn
			}
		}
	}
	return blockValidationResult;
}

function validateNotch(notchNumber , slotContents){
	var notchValidationResult = true;

	if(slotContents.length == 0) {
        notchValidationResult = notchProperties[notchNumber].canBeEmpty;

        if(! notchValidationResult) {
			alert("slot " + notchNumber + " needs to have something in it");
		}
	} else{
		if(notchProperties[notchNumber].isRepeatable){
			if(notchProperties[notchNumber].isGrouped){   // one/zeroOrMore has interleave as only child

			} else{                             // one/zeroOrMore has choice, optional as only child

			}
		} else{
			if(notchProperties[notchNumber].isGrouped){    // interleave notch notch

			} else{                              //optional, choice notch
				notchValidationResult = validateChoiceNotch(notchNumber, slotContents);
			}
		}
	}
	return notchValidationResult;
}

function validateChoiceNotch(notchNumber, slotContents){
	var expectedChildren = JSON.parse( JSON.stringify( notchProperties[notchNumber].childrenInfo ) );
    var actualChildren = getPrettyNamesOfSlotContents(slotContents);
	if( actualChildren.length == 1 ){
        if( expectedChildren.indexOf( actualChildren[0] ) != -1 ){  //if child is directly mentioned in expectedChildren, it is not an interleave
			return true;
        }
    }

    if( isRepetitiveChild( expectedChildren , actualChildren[0]) ){ //choice has a oneOrMore child
        var iterator = 1;
        while( iterator < actualChildren.length ){
            if( actualChildren[0] == actualChildren[iterator] ){
                iterator++;
                continue;
            } else{
                alert("Please attach only one type of repetitive block");
                return false;
            }
        }
    } else{
        var interleaveLists = isInterleaveChild(expectedChildren , actualChildren[0]);
        if(interleaveLists.length>0){   //choice has an interleave child
            for(var i=0;i<interleaveLists.length;i++){
                var currentList = JSON.parse(interleaveLists[i]);
                var ans = false;
                if(currentList.length != actualChildren.length){
                    /*if we have two interleaves in the choice [a,b] and [a,b,c],
                     *then we proceed ahead only for that list which has the same number of elements as the current notch
                     */
                     ans = false;
                    continue;
                }
                ans = true;
                while(currentList.length!=0){   //we may be missing interleave containing a repetitive block
                    if( actualChildren.indexOf(currentList[0]) != -1 ){
                        currentList.splice(0,1);
                    } else{
                        ans = false;
                        break;
                    }
                }
                if(ans == true){
                    return true;
                }
            }
            alert("The interleave has not been implemented properly.");
            return false;
        } else{ //if block occurs multiple times but is neither a repetitive nor interleave block
			alert("You cannot attach this block more than once");
			return false;
		}
    }
	return true;
}

function isRepetitiveChild(expectedChildren , name){
    var ans = false;
    var index = expectedChildren.indexOf(name);
    var i = index-1;
    while(i>=0){
        if( expectedChildren[i] == "startRepetition_" ){
            break;
        }
        i--;
    }
    if(i>=0){
        while(i<expectedChildren.length){
            if( expectedChildren[i] == "_endRepetition" ){
                break;
            }
            i++;
        }
        if(i>index && i!=expectedChildren.length){
            ans = true;
        }
    }
    return ans;
}

function isInterleaveChild( expectedChildren , name ){
    var listsThatChildCanBePartOf = [];
    for(var i=0;i<expectedChildren.length;i++){
        if( Object.prototype.toString.call ( expectedChildren[i] ) === "[object Array]" ){
            if(expectedChildren[i].indexOf(name) != -1 ){
                listsThatChildCanBePartOf.push(JSON.stringify(expectedChildren[i]));
            }
        }
    }
    return listsThatChildCanBePartOf;

}

function getPrettyNamesOfSlotContents(blockArray){
	var ans = [];
	for(var i=0; i<blockArray.length; i++){
		ans.push( blockNameToDisplayNameMapper[blockArray[i].type] );
	}
	return ans;
}
<|endoftext|>


<commit_before>import * as utils from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE} from '../src/mediaTypes.js';

const BIDDER_CODE = 'mediaforce';
const ENDPOINT_URL = 'https://rtb.mfadsrvr.com/header_bid';
const TEST_ENDPOINT_URL = 'https://staging-gce-sc-2.dorpat.iponweb.net/header_bid?skip_st=1&skip_aes=1&skip_bl=1';
const NATIVE_ID_MAP = {};
const NATIVE_PARAMS = {
  title: {
    id: 1,
    name: 'title'
  },
  icon: {
    id: 2,
    type: 1,
    name: 'img'
  },
  image: {
    id: 3,
    type: 3,
    name: 'img'
  },
  body: {
    id: 4,
    name: 'data',
    type: 2
  },
  sponsoredBy: {
    id: 5,
    name: 'data',
    type: 1
  },
  cta: {
    id: 6,
    type: 12,
    name: 'data'
  },
  body2: {
    id: 7,
    name: 'data',
    type: 10
  },
  rating: {
    id: 8,
    name: 'data',
    type: 3
  },
  likes: {
    id: 9,
    name: 'data',
    type: 4
  },
  downloads: {
    id: 10,
    name: 'data',
    type: 5
  },
  displayUrl: {
    id: 11,
    name: 'data',
    type: 11
  },
  price: {
    id: 12,
    name: 'data',
    type: 6
  },
  salePrice: {
    id: 13,
    name: 'data',
    type: 7
  },
  address: {
    id: 14,
    name: 'data',
    type: 9
  },
  phone: {
    id: 15,
    name: 'data',
    type: 8
  }
};
Object.keys(NATIVE_PARAMS).forEach((key) => {
  NATIVE_ID_MAP[NATIVE_PARAMS[key].id] = key;
});

export const spec = {
  code: BIDDER_CODE,
  supportedMediaTypes: [BANNER, NATIVE],

  /**
   * Determines whether or not the given bid request is valid.
   *
   * @param {BidRequest} bid The bid params to validate.
   * @return boolean True if this is a valid bid, and false otherwise.
   */
  isBidRequestValid: function(bid) {
    return !!((typeof bid.params === 'object') && bid.params.placement_id && bid.params.publisher_id);
  },

  /**
   * Make a server request from the list of BidRequests.
   *
   * @param {BidRequest[]} validBidRequests - an array of bids
   * @param {bidderRequest} bidderRequest bidder request object
   * @return ServerRequest Info describing the request to the server.
   */
  buildRequests: function(validBidRequests, bidderRequest) {
    if (validBidRequests.length === 0) {
      return;
    }

    const referer = bidderRequest && bidderRequest.refererInfo ? encodeURIComponent(bidderRequest.refererInfo.referer) : '';
    const dnt = utils.getDNT() ? 1 : 0;
    let requests = [];
    validBidRequests.forEach(bid => {
      let tagid = bid.params.placement_id;
      let bidfloor = bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : 0;
      let imp = [];
      let validImp = false;
      let impObj = {
        id: bid.bidId,
        tagid: tagid,
        secure: 1,
        bidfloor: bidfloor,
      };
      for (let mediaTypes in bid.mediaTypes) {
        switch (mediaTypes) {
          case BANNER:
            impObj.banner = createBannerRequest(bid);
            validImp = true;
            break;
          case NATIVE:
            impObj.native = createNativeRequest(bid);
            validImp = true;
            break;
          default: return;
        }
      }
      validImp && imp.push(impObj);

      let request = {
        id: bid.transactionId,
        site: {
          page: referer,
          ref: referer,
          id: bid.params.publisher_id,
          publisher: {
            id: bid.params.publisher_id
          },
        },
        device: {
          ua: navigator.userAgent,
          js: 1,
          dnt: dnt,
          language: getLanguage()
        },
        imp
      };
      requests.push({
        method: 'POST',
        url: bid.params.is_test ? TEST_ENDPOINT_URL : ENDPOINT_URL,
        /* options: {
          customHeaders: {'Authorization': 'Basic ZG9ycGF0LWRiZzptWUNtT0NraHFB'}
        }, */
        data: JSON.stringify(request)
      });
    });
    return requests;
  },

  /**
   * Unpack the response from the server into a list of bids.
   *
   * @param {ServerResponse} serverResponse A successful response from the server.
   * @param {BidRequest} bidRequest
   * @return {Bid[]} An array of bids which were nested inside the server.
   */
  interpretResponse: function(serverResponse, bidRequest) {
    if (!serverResponse || !serverResponse.body) {
      return [];
    }

    const responseBody = serverResponse.body;
    const bidResponses = [];
    const cur = responseBody.cur;
    responseBody.seatbid.forEach((bids) => {
      bids.bid.forEach((serverBid) => {
        const bid = {
          requestId: serverBid.impid,
          cpm: parseFloat(serverBid.price),
          creativeId: serverBid.adid,
          currency: cur,
          netRevenue: true,
          ttl: serverBid.ttl || 300,
          burl: serverBid.burl,
        };
        if (serverBid.dealid) {
          bid.dealId = serverBid.dealid;
        }
        if (serverBid.adm) {
          bid.width = serverBid.w;
          bid.height = serverBid.h;
          bid.ad = serverBid.adm;
          bid.mediaType = BANNER;
        } else if (serverBid.ext && serverBid.ext.native) {
          bid.native = parseNative(serverBid.ext.native);
          bid.mediaType = NATIVE;
        }

        bidResponses.push(bid);
      })
    });

    return bidResponses;
  },

  /**
   * Register bidder specific code, which will execute if a bid from this bidder won the auction
   * @param {Bid} The bid that won the auction
   */
  onBidWon: function(bid) {
    const cpm = utils.deepAccess(bid, 'adserverTargeting.hb_pb') || '';
    if (utils.isStr(bid.burl) && bid.burl !== '') {
      bid.burl = utils.replaceAuctionPrice(bid.burl, cpm);
      utils.triggerPixel(bid.burl);
    }
  },
}
registerBidder(spec);

function getLanguage() {
  const language = navigator.language ? 'language' : 'userLanguage';
  return navigator[language].split('-')[0];
}

function createBannerRequest(bid) {
  const sizes = bid.mediaTypes.banner.sizes;
  if (!sizes.length) return;

  let format = [];
  let r = utils.parseGPTSingleSizeArrayToRtbSize(sizes[0]);
  for (let f = 1; f < sizes.length; f++) {
    format.push(utils.parseGPTSingleSizeArrayToRtbSize(sizes[f]));
  }
  if (format.length) {
    r.format = format
  }
  return r
}

function parseNative(native) {
  const {assets, link, imptrackers, jstracker} = native;
  const result = {
    clickUrl: link.url,
    clickTrackers: link.clicktrackers || [],
    impressionTrackers: imptrackers || [],
    javascriptTrackers: jstracker ? [jstracker] : []
  };
  (assets || []).forEach((asset) => {
    const {id, img, data, title} = asset;
    const key = NATIVE_ID_MAP[id];
    if (key) {
      if (!utils.isEmpty(title)) {
        result.title = title.text
      } else if (!utils.isEmpty(img)) {
        result[key] = {
          url: img.url,
          height: img.h,
          width: img.w
        }
      } else if (!utils.isEmpty(data)) {
        result[key] = data.value;
      }
    }
  });

  return result;
}

function createNativeRequest(bid) {
  const assets = [];
  if (bid.nativeParams) {
    Object.keys(bid.nativeParams).forEach((key) => {
      if (NATIVE_PARAMS[key]) {
        const {name, type, id} = NATIVE_PARAMS[key];
        const assetObj = type ? {type} : {};
        let {len, sizes, required, aspect_ratios: aRatios} = bid.nativeParams[key];
        if (len) {
          assetObj.len = len;
        }
        if (aRatios && aRatios[0]) {
          aRatios = aRatios[0];
          let wmin = aRatios.min_width || 0;
          let hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0;
          assetObj.wmin = wmin;
          assetObj.hmin = hmin;
        }
        if (sizes && sizes.length) {
          sizes = [].concat(...sizes);
          assetObj.w = sizes[0];
          assetObj.h = sizes[1];
        }
        const asset = {required: required ? 1 : 0, id};
        asset[name] = assetObj;
        assets.push(asset);
      }
    });
  }
  return {
    ver: '1.2',
    request: {
      assets: assets,
      context: 1,
      plcmttype: 1,
      ver: '1.2'
    }
  }
}
<commit_msg>Update Mediaforce Bid Adapter<commit_after>import * as utils from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE} from '../src/mediaTypes.js';

const BIDDER_CODE = 'mediaforce';
const ENDPOINT_URL = 'https://rtb.mfadsrvr.com/header_bid';
const TEST_ENDPOINT_URL = 'http://staging-gce-sc-2.dorpat.iponweb.net/header_bid';
const NATIVE_ID_MAP = {};
const NATIVE_PARAMS = {
  title: {
    id: 1,
    name: 'title'
  },
  icon: {
    id: 2,
    type: 1,
    name: 'img'
  },
  image: {
    id: 3,
    type: 3,
    name: 'img'
  },
  body: {
    id: 4,
    name: 'data',
    type: 2
  },
  sponsoredBy: {
    id: 5,
    name: 'data',
    type: 1
  },
  cta: {
    id: 6,
    type: 12,
    name: 'data'
  },
  body2: {
    id: 7,
    name: 'data',
    type: 10
  },
  rating: {
    id: 8,
    name: 'data',
    type: 3
  },
  likes: {
    id: 9,
    name: 'data',
    type: 4
  },
  downloads: {
    id: 10,
    name: 'data',
    type: 5
  },
  displayUrl: {
    id: 11,
    name: 'data',
    type: 11
  },
  price: {
    id: 12,
    name: 'data',
    type: 6
  },
  salePrice: {
    id: 13,
    name: 'data',
    type: 7
  },
  address: {
    id: 14,
    name: 'data',
    type: 9
  },
  phone: {
    id: 15,
    name: 'data',
    type: 8
  }
};
Object.keys(NATIVE_PARAMS).forEach((key) => {
  NATIVE_ID_MAP[NATIVE_PARAMS[key].id] = key;
});

export const spec = {
  code: BIDDER_CODE,
  supportedMediaTypes: [BANNER, NATIVE],

  /**
   * Determines whether or not the given bid request is valid.
   *
   * @param {BidRequest} bid The bid params to validate.
   * @return boolean True if this is a valid bid, and false otherwise.
   */
  isBidRequestValid: function(bid) {
    return !!((typeof bid.params === 'object') && bid.params.placement_id && bid.params.publisher_id);
  },

  /**
   * Make a server request from the list of BidRequests.
   *
   * @param {BidRequest[]} validBidRequests - an array of bids
   * @param {bidderRequest} bidderRequest bidder request object
   * @return ServerRequest Info describing the request to the server.
   */
  buildRequests: function(validBidRequests, bidderRequest) {
    if (validBidRequests.length === 0) {
      return;
    }

    const referer = bidderRequest && bidderRequest.refererInfo ? encodeURIComponent(bidderRequest.refererInfo.referer) : '';
    const dnt = utils.getDNT() ? 1 : 0;
    let requests = [];
    validBidRequests.forEach(bid => {
      let tagid = bid.params.placement_id;
      let bidfloor = bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : 0;
      let imp = [];
      let validImp = false;
      let impObj = {
        id: bid.bidId,
        tagid: tagid,
        secure: 1,
        bidfloor: bidfloor,
      };
      for (let mediaTypes in bid.mediaTypes) {
        switch (mediaTypes) {
          case BANNER:
            impObj.banner = createBannerRequest(bid);
            validImp = true;
            break;
          case NATIVE:
            impObj.native = createNativeRequest(bid);
            validImp = true;
            break;
          default: return;
        }
      }
      validImp && imp.push(impObj);

      let request = {
        id: bid.transactionId,
        site: {
          page: referer,
          ref: referer,
          id: bid.params.publisher_id,
          publisher: {
            id: bid.params.publisher_id
          },
        },
        device: {
          ua: navigator.userAgent,
          js: 1,
          dnt: dnt,
          language: getLanguage()
        },
        imp
      };
      requests.push({
        method: 'POST',
        url: bid.params.is_test ? TEST_ENDPOINT_URL : ENDPOINT_URL,
        data: JSON.stringify(request)
      });
    });
    return requests;
  },

  /**
   * Unpack the response from the server into a list of bids.
   *
   * @param {ServerResponse} serverResponse A successful response from the server.
   * @param {BidRequest} bidRequest
   * @return {Bid[]} An array of bids which were nested inside the server.
   */
  interpretResponse: function(serverResponse, bidRequest) {
    if (!serverResponse || !serverResponse.body) {
      return [];
    }

    const responseBody = serverResponse.body;
    const bidResponses = [];
    const cur = responseBody.cur;
    responseBody.seatbid.forEach((bids) => {
      bids.bid.forEach((serverBid) => {
        const bid = {
          requestId: serverBid.impid,
          cpm: parseFloat(serverBid.price),
          creativeId: serverBid.adid,
          currency: cur,
          netRevenue: true,
          ttl: serverBid.ttl || 300,
          burl: serverBid.burl,
        };
        if (serverBid.dealid) {
          bid.dealId = serverBid.dealid;
        }
        let jsonAdm;
        let adm = serverBid.adm;
        let ext = serverBid.ext;
        try {
          jsonAdm = JSON.parse(adm);
        } catch(err) {}
        if (jsonAdm && jsonAdm.native) {
          ext = ext || {};
          ext.native = jsonAdm.native;
          adm = null;
        }
        if (adm) {
          bid.width = serverBid.w;
          bid.height = serverBid.h;
          bid.ad = adm;
          bid.mediaType = BANNER;
        } else if (ext && ext.native) {
          bid.native = parseNative(ext.native);
          bid.mediaType = NATIVE;
        }

        bidResponses.push(bid);
      })
    });

    return bidResponses;
  },

  /**
   * Register bidder specific code, which will execute if a bid from this bidder won the auction
   * @param {Bid} The bid that won the auction
   */
  onBidWon: function(bid) {
    const cpm = utils.deepAccess(bid, 'adserverTargeting.hb_pb') || '';
    if (utils.isStr(bid.burl) && bid.burl !== '') {
      bid.burl = utils.replaceAuctionPrice(bid.burl, cpm);
      utils.triggerPixel(bid.burl);
    }
  },
}
registerBidder(spec);

function getLanguage() {
  const language = navigator.language ? 'language' : 'userLanguage';
  return navigator[language].split('-')[0];
}

function createBannerRequest(bid) {
  const sizes = bid.mediaTypes.banner.sizes;
  if (!sizes.length) return;

  let format = [];
  let r = utils.parseGPTSingleSizeArrayToRtbSize(sizes[0]);
  for (let f = 1; f < sizes.length; f++) {
    format.push(utils.parseGPTSingleSizeArrayToRtbSize(sizes[f]));
  }
  if (format.length) {
    r.format = format
  }
  return r
}

function parseNative(native) {
  const {assets, link, imptrackers, jstracker} = native;
  const result = {
    clickUrl: link.url,
    clickTrackers: link.clicktrackers || [],
    impressionTrackers: imptrackers || [],
    javascriptTrackers: jstracker ? [jstracker] : []
  };
  (assets || []).forEach((asset) => {
    const {id, img, data, title} = asset;
    const key = NATIVE_ID_MAP[id];
    if (key) {
      if (!utils.isEmpty(title)) {
        result.title = title.text
      } else if (!utils.isEmpty(img)) {
        result[key] = {
          url: img.url,
          height: img.h,
          width: img.w
        }
      } else if (!utils.isEmpty(data)) {
        result[key] = data.value;
      }
    }
  });

  return result;
}

function createNativeRequest(bid) {
  const assets = [];
  if (bid.nativeParams) {
    Object.keys(bid.nativeParams).forEach((key) => {
      if (NATIVE_PARAMS[key]) {
        const {name, type, id} = NATIVE_PARAMS[key];
        const assetObj = type ? {type} : {};
        let {len, sizes, required, aspect_ratios: aRatios} = bid.nativeParams[key];
        if (len) {
          assetObj.len = len;
        }
        if (aRatios && aRatios[0]) {
          aRatios = aRatios[0];
          let wmin = aRatios.min_width || 0;
          let hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0;
          assetObj.wmin = wmin;
          assetObj.hmin = hmin;
        }
        if (sizes && sizes.length) {
          sizes = [].concat(...sizes);
          assetObj.w = sizes[0];
          assetObj.h = sizes[1];
        }
        const asset = {required: required ? 1 : 0, id};
        asset[name] = assetObj;
        assets.push(asset);
      }
    });
  }
  return {
    ver: '1.2',
    request: {
      assets: assets,
      context: 1,
      plcmttype: 1,
      ver: '1.2'
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Created by zenghong on 2017/4/26.
 */
var baseUrl = 'http://' + window.location.host;

$(function () {
  $('.detail-back').click(function () {
    window.history.back();
  });

  var list = $('.list-item');

  console.log(window.localStorage.getItem('tender_id'));
  var tenderId = window.localStorage.getItem('tender_id');
  getTenderDetail();
  function getTenderDetail() {
    $.ajax({
      url: baseUrl + '/tender/user/getTenderByTenderId',
      method: 'post',
      data: {tender_id: tenderId},
      success: function (data) {
        console.log(data);
        if (!data.err) {
          var top =
            $('
' +
              '
' +
              '首款金额' +
              '
' +
              '
' +
              data.real_pay_top_cash +
              '
' +
              '
' +
              getTiaozhangStr(data.real_pay_top_tiaozhangs) +
              '
' +
              '
' +
              '已通过' +
              '
' +
              '
');

          if (!data.real_pay_top_cash_time) {
            top.find('.verify-btn').removeClass('pass').text('通过');
            top.find('.verify-btn').click(function () {
              payment('real_pay_top_cash');
            });
          }

          list.append(top);

          var tail =
            $('
' +
              '
' +
              '尾款金额' +
              '
' +
              '
' +
              data.real_pay_tail_cash +
              '
' +
              '
' +
              getTiaozhangStr(data.real_pay_tail_tiaozhangs) +
              '
' +
              '
' +
              '已通过' +
              '
' +
              '
');

          if (!data.real_pay_tail_cash_time) {
            tail.find('.verify-btn').removeClass('pass').text('通过');
            tail.find('.verify-btn').click(function () {
              payment('real_pay_tail_cash');
            });
          }

          list.append(tail);

          var last =
            $('
' +
              '
' +
              '回单金额' +
              '
' +
              '
' +
              data.real_pay_last_cash +
              '
' +
              '
' +
              getTiaozhangStr(data.real_pay_last_tiaozhangs) +
              '
' +
              '
' +
              '已通过' +
              '
' +
              '
');

          if (!data.real_pay_last_cash_time) {
            last.find('.verify-btn').removeClass('pass').text('通过');
            last.find('.verify-btn').click(function () {
              payment('real_pay_last_cash');
            });
          }

          list.append(last);

          var ya_jin =
            $('
' +
              '
' +
              '押金金额' +
              '
' +
              '
' +
              data.real_pay_ya_jin +
              '
' +
              '
' +
              getTiaozhangStr(data.real_pay_ya_jin_tiaozhangs) +
              '
' +
              '
' +
              '已通过' +
              '
' +
              '
');

          if (!data.real_pay_ya_jin_cash_time) {
            ya_jin.find('.verify-btn').removeClass('pass').text('通过');
            ya_jin.find('.verify-btn').click(function () {
              payment('real_pay_ya_jin');
            });
          }

          list.append(ya_jin);
        }
      }
    });
  }

  function payment(type) {
    $.ajax({
      url: baseUrl + '/tender/user/newPayment',
      data: {
        type: type,
        tender_id: tenderId
      },
      method: 'post',
      success: function (data) {
        if (!data.err) {
          alert('操作成功！');
          window.location = window.location;
        }
        else {
          alert(data.err.type);
        }
      }

    });
  }

  function getTiaozhangStr(items) {
    str = '';

    items = items || [];
    items.forEach(function (item) {
      str +=
        '
' +
        '
' +
        item.price +
        '
' +
        '
' +
        item.reason +
        '
' +
        '
';
    });

    if (!str) {
      str = '
' +
        '
' +
        '无' +
        '
' +
        '
' +
        '无' +
        '
' +
        '
';
    }
    return str;
  }
});<commit_msg>Update for test<commit_after>/**
 * Created by zenghong on 2017/4/26.
 */
var baseUrl = 'http://' + window.location.host;

$(function () {
  $('.detail-back').click(function () {
    window.history.back();
  });

  var list = $('.list-item');

  console.log(window.localStorage.getItem('tender_id'));
  var tenderId = window.localStorage.getItem('tender_id');
  getTenderDetail();
  function getTenderDetail() {
    $.ajax({
      url: baseUrl + '/tender/user/getTenderByTenderId',
      method: 'post',
      data: {tender_id: tenderId},
      success: function (data) {
        console.log(data);
        if (!data.err) {
          var top =
            $('
' +
              '
' +
              '首款金额' +
              '
' +
              '
' +
              data.real_pay_top_cash +
              '
' +
              '
' +
              getTiaozhangStr(data.real_pay_top_tiaozhangs) +
              '
' +
              '
' +
              '已通过' +
              '
' +
              '
');

          if (!data.real_pay_top_cash_time) {
            top.find('.verify-btn').removeClass('pass').text('通过');
            top.find('.verify-btn').click(function () {
              payment('real_pay_top_cash');
            });
          }

          list.append(top);

          var tail =
            $('
' +
              '
' +
              '尾款金额' +
              '
' +
              '
' +
              data.real_pay_tail_cash +
              '
' +
              '
' +
              getTiaozhangStr(data.real_pay_tail_tiaozhangs) +
              '
' +
              '
' +
              '已通过' +
              '
' +
              '
');

          if (!data.real_pay_tail_cash_time) {
            tail.find('.verify-btn').removeClass('pass').text('通过');
            tail.find('.verify-btn').click(function () {
              payment('real_pay_tail_cash');
            });
          }

          list.append(tail);

          var last =
            $('
' +
              '
' +
              '回单金额' +
              '
' +
              '
' +
              data.real_pay_last_cash +
              '
' +
              '
' +
              getTiaozhangStr(data.real_pay_last_tiaozhangs) +
              '
' +
              '
' +
              '已通过' +
              '
' +
              '
');

          if (!data.real_pay_last_cash_time) {
            last.find('.verify-btn').removeClass('pass').text('通过');
            last.find('.verify-btn').click(function () {
              payment('real_pay_last_cash');
            });
          }

          list.append(last);

          var ya_jin =
            $('
' +
              '
' +
              '押金金额' +
              '
' +
              '
' +
              data.real_pay_ya_jin +
              '
' +
              '
' +
              getTiaozhangStr(data.real_pay_ya_jin_tiaozhangs) +
              '
' +
              '
' +
              '已通过' +
              '
' +
              '
');

          if (!data.real_pay_ya_jin_time) {
            ya_jin.find('.verify-btn').removeClass('pass').text('通过');
            ya_jin.find('.verify-btn').click(function () {
              payment('real_pay_ya_jin');
            });
          }

          list.append(ya_jin);
        }
      }
    });
  }

  function payment(type) {
    $.ajax({
      url: baseUrl + '/tender/user/newPayment',
      data: {
        type: type,
        tender_id: tenderId
      },
      method: 'post',
      success: function (data) {
        if (!data.err) {
          alert('操作成功！');
          window.location = window.location;
        }
        else {
          alert(data.err.type);
        }
      }

    });
  }

  function getTiaozhangStr(items) {
    str = '';

    items = items || [];
    items.forEach(function (item) {
      str +=
        '
' +
        '
' +
        item.price +
        '
' +
        '
' +
        item.reason +
        '
' +
        '
';
    });

    if (!str) {
      str = '
' +
        '
' +
        '无' +
        '
' +
        '
' +
        '无' +
        '
' +
        '
';
    }
    return str;
  }
});<|endoftext|>


<commit_before>(function() {
    var Ext = window.Ext4 || window.Ext;

    Ext.define('Rally.apps.customlist.CustomListApp', {
        extend: 'Rally.app.GridBoardApp',
        requires: [
            'Deft.Promise',
            'Rally.apps.customlist.Settings',
            'Rally.data.ModelTypes',
            'Rally.data.util.Sorter',
            'Rally.data.wsapi.Filter'
        ],

        statePrefix: 'customlist',
        disallowedAddNewTypes: ['user', 'userprofile', 'useriterationcapacity', 'testcaseresult', 'task', 'scmrepository', 'project', 'changeset', 'change', 'builddefinition', 'build'],

        initComponent: function () {
            this.defaultSettings = {
                query: this.appContainer.query,
                order: this.appContainer.order,
                type: this.appContainer.url,
                columnNames: (this.appContainer.fetch || '').split(',')
            };
            this.callParent(arguments);
        },

        getSettingsFields: function() {
            return Rally.apps.customlist.Settings.getFields(this.getContext());
        },

        loadModelNames: function () {
            this.modelNames = _.compact([this.getSetting('type')]);
            this._setColumnNames(this.getSetting('columnNames'));
            return Deft.Promise.when(this.modelNames);
        },

        loadGridBoard: function () {
            if (_.isEmpty(this.modelNames)) {
                Ext.defer(function () {
                    this.owner.showSettings();
                    this.publishComponentReady();
                }, 1, this);
            } else {
                this.enableAddNew = this._shouldEnableAddNew();
                this.callParent(arguments);
            }
        },

        getGridConfig: function () {
            return _.merge(this.callParent(arguments), {
                listeners: {
                    beforestaterestore: this._onBeforeGridStateRestore,
                    beforestatesave: this._onBeforeGridStateSave,
                    scope: this
                }
            });
        },

        getGridStoreConfig: function () {
            var sorters = this._getValidSorters(Rally.data.util.Sorter.sorters(this.getSetting('order')));

            if (_.isEmpty(sorters)) {
                var rankField = this.getContext().getWorkspace().WorkspaceConfiguration.DragDropRankingEnabled ? 'DragAndDropRank' : 'Rank';
                var defaultSort = Rally.data.ModelTypes.areArtifacts(this.modelNames) ? rankField : Rally.data.util.Sorter.getDefaultSort(this.modelNames[0]);

                sorters = Rally.data.util.Sorter.sorters(defaultSort);
            }

            return _.merge({
                sorters: sorters
            }, this.appContainer.pagesize ? { pageSize: this.appContainer.pagesize } : {});
        },

        getAddNewConfig: function () {
            return _.merge(this.callParent(arguments), {
                minWidth: 700,
                openEditorAfterAddFailure: false
            });
        },

        getFieldPickerConfig: function () {
            return _.merge(this.callParent(arguments), {
                buttonConfig: {
                    disabled: !this._userHasPermissionsToEditPanelSettings()
                }
            });
        },

        getPermanentFilters: function () {
            var projectFilter = this.getSetting('type').toLowerCase() === 'milestone' ? [
                Rally.data.wsapi.Filter.or([
                    { property: 'Projects', operator: 'contains', value: this.getContext().getProjectRef() },
                    { property: 'TargetProject', operator: '=', value: null }
                ])
            ] : null;

            var query = this.getSetting('query');
            var timeboxScopeFilter = this._getTimeboxScopeFilter().concat(query ? [ Rally.data.wsapi.Filter.fromQueryString(query) ] : []);
            return projectFilter ? projectFilter.concat(timeboxScopeFilter) : timeboxScopeFilter;
        },

        onTimeboxScopeChange: function() {
            this.callParent(arguments);
            this.loadGridBoard();
        },

        _getTimeboxScopeFilter: function () {
            var timeboxScope = this.getContext().getTimeboxScope();
            var hasTimeboxField = timeboxScope && _.any(this.models, function (model) {
                return model.hasField(Ext.String.capitalize(timeboxScope.getType()));
            });
            return hasTimeboxField ? [ timeboxScope.getQueryFilter() ] : [];
        },

        _shouldEnableAddNew: function() {
            return !_.contains(this.disallowedAddNewTypes, this.getSetting('type').toLowerCase());
        },

        _setColumnNames: function (columnNames) {
            columnNames = _.isString(columnNames) ? columnNames.split(',') : columnNames;

            // Note: When enableHierarchy is true (artifacts only for now), the FormattedID column is always added
            // If we include FormattedID in columns names, we get 2 ID columns, which is poopy.
            // This problem should be fixed for reals in the SDK somewhere eventually.
            if (Rally.data.ModelTypes.areArtifacts(this.modelNames)) {
                columnNames = _.reject(columnNames, function (columnName) {
                    return _.isString(columnName) && columnName.toLowerCase() === 'formattedid';
                });
            }

            this.columnNames = _.compact(columnNames);
        },

        _onBeforeGridStateRestore: function (grid, state) {
            if (!state) {
                return;
            }

            if (state.columns) {
                var appScopedColumnNames = this.getColumnCfgs();
                var userScopedColumnNames = this._getColumnNamesFromState(state);

                if(_.difference(appScopedColumnNames, userScopedColumnNames).length > 0) {
                    state.columns = appScopedColumnNames;
                } else {
                    state.columns = _.filter(state.columns, function (column) {
                        return _.contains(appScopedColumnNames, _.isObject(column) ? column.dataIndex : column);
                    });
                }
            }

            if (state.sorters) {
                state.sorters = this._getValidSorters(state.sorters);
                if (_.isEmpty(state.sorters)) {
                    delete state.sorters;
                }
            }
        },

        _onBeforeGridStateSave: function (grid, state) {
            var newColumnNames = this._getColumnNamesFromState(state);

            if (!_.isEmpty(newColumnNames)) {
                this._setColumnNames(newColumnNames);

                if (this._userHasPermissionsToEditPanelSettings()) {
                    this.updateSettingsValues({
                        settings: {
                            columnNames: newColumnNames.join(',')
                        }
                    });
                }
            }
        },

        _getValidSorters: function (sorters) {
            return _.filter(sorters, function (sorter) {
                return _.any(this.models, function (model) {
                    var field = model.getField(sorter.property);
                    return field && field.sortable;
                });
            }, this);
        },

        _userHasPermissionsToEditPanelSettings: function () {
            return this.appContainer.isEditable;
        },

        _getColumnNamesFromState: function (state) {
            return _(state && state.columns).map(function (newColumn) {
                return _.isObject(newColumn) ? newColumn.dataIndex : newColumn;
            }).compact().value();
        }
    });
})();
<commit_msg>DE24048: In FullScreen mode the Custom List app does not allow access to column picker<commit_after>(function() {
    var Ext = window.Ext4 || window.Ext;

    Ext.define('Rally.apps.customlist.CustomListApp', {
        extend: 'Rally.app.GridBoardApp',
        requires: [
            'Deft.Promise',
            'Rally.apps.customlist.Settings',
            'Rally.data.ModelTypes',
            'Rally.data.util.Sorter',
            'Rally.data.wsapi.Filter'
        ],

        statePrefix: 'customlist',
        disallowedAddNewTypes: ['user', 'userprofile', 'useriterationcapacity', 'testcaseresult', 'task', 'scmrepository', 'project', 'changeset', 'change', 'builddefinition', 'build'],

        initComponent: function () {
            this.defaultSettings = {
                query: this.appContainer.query,
                order: this.appContainer.order,
                type: this.appContainer.url,
                columnNames: (this.appContainer.fetch || '').split(',')
            };
            this.callParent(arguments);
        },

        getSettingsFields: function() {
            return Rally.apps.customlist.Settings.getFields(this.getContext());
        },

        loadModelNames: function () {
            this.modelNames = _.compact([this.getSetting('type')]);
            this._setColumnNames(this.getSetting('columnNames'));
            return Deft.Promise.when(this.modelNames);
        },

        loadGridBoard: function () {
            if (_.isEmpty(this.modelNames)) {
                Ext.defer(function () {
                    this.owner.showSettings();
                    this.publishComponentReady();
                }, 1, this);
            } else {
                this.enableAddNew = this._shouldEnableAddNew();
                this.callParent(arguments);
            }
        },

        getGridConfig: function () {
            return _.merge(this.callParent(arguments), {
                listeners: {
                    beforestaterestore: this._onBeforeGridStateRestore,
                    beforestatesave: this._onBeforeGridStateSave,
                    scope: this
                }
            });
        },

        getGridStoreConfig: function () {
            var sorters = this._getValidSorters(Rally.data.util.Sorter.sorters(this.getSetting('order')));

            if (_.isEmpty(sorters)) {
                var rankField = this.getContext().getWorkspace().WorkspaceConfiguration.DragDropRankingEnabled ? 'DragAndDropRank' : 'Rank';
                var defaultSort = Rally.data.ModelTypes.areArtifacts(this.modelNames) ? rankField : Rally.data.util.Sorter.getDefaultSort(this.modelNames[0]);

                sorters = Rally.data.util.Sorter.sorters(defaultSort);
            }

            return _.merge({
                sorters: sorters
            }, this.appContainer.pagesize ? { pageSize: this.appContainer.pagesize } : {});
        },

        getAddNewConfig: function () {
            return _.merge(this.callParent(arguments), {
                minWidth: 700,
                openEditorAfterAddFailure: false
            });
        },

        getFieldPickerConfig: function () {
            return _.merge(this.callParent(arguments), {
                buttonConfig: {
                    disabled: !this._userHasPermissionsToEditPanelSettings()
                }
            });
        },

        getPermanentFilters: function () {
            var projectFilter = this.getSetting('type').toLowerCase() === 'milestone' ? [
                Rally.data.wsapi.Filter.or([
                    { property: 'Projects', operator: 'contains', value: this.getContext().getProjectRef() },
                    { property: 'TargetProject', operator: '=', value: null }
                ])
            ] : null;

            var query = this.getSetting('query');
            var timeboxScopeFilter = this._getTimeboxScopeFilter().concat(query ? [ Rally.data.wsapi.Filter.fromQueryString(query) ] : []);
            return projectFilter ? projectFilter.concat(timeboxScopeFilter) : timeboxScopeFilter;
        },

        onTimeboxScopeChange: function() {
            this.callParent(arguments);
            this.loadGridBoard();
        },

        _getTimeboxScopeFilter: function () {
            var timeboxScope = this.getContext().getTimeboxScope();
            var hasTimeboxField = timeboxScope && _.any(this.models, function (model) {
                return model.hasField(Ext.String.capitalize(timeboxScope.getType()));
            });
            return hasTimeboxField ? [ timeboxScope.getQueryFilter() ] : [];
        },

        _shouldEnableAddNew: function() {
            return !_.contains(this.disallowedAddNewTypes, this.getSetting('type').toLowerCase());
        },

        _setColumnNames: function (columnNames) {
            columnNames = _.isString(columnNames) ? columnNames.split(',') : columnNames;

            // Note: When enableHierarchy is true (artifacts only for now), the FormattedID column is always added
            // If we include FormattedID in columns names, we get 2 ID columns, which is poopy.
            // This problem should be fixed for reals in the SDK somewhere eventually.
            if (Rally.data.ModelTypes.areArtifacts(this.modelNames)) {
                columnNames = _.reject(columnNames, function (columnName) {
                    return _.isString(columnName) && columnName.toLowerCase() === 'formattedid';
                });
            }

            this.columnNames = _.compact(columnNames);
        },

        _onBeforeGridStateRestore: function (grid, state) {
            if (!state) {
                return;
            }

            if (state.columns) {
                var appScopedColumnNames = this.getColumnCfgs();
                var userScopedColumnNames = this._getColumnNamesFromState(state);

                if(_.difference(appScopedColumnNames, userScopedColumnNames).length > 0) {
                    state.columns = appScopedColumnNames;
                } else {
                    state.columns = _.filter(state.columns, function (column) {
                        return _.contains(appScopedColumnNames, _.isObject(column) ? column.dataIndex : column);
                    });
                }
            }

            if (state.sorters) {
                state.sorters = this._getValidSorters(state.sorters);
                if (_.isEmpty(state.sorters)) {
                    delete state.sorters;
                }
            }
        },

        _onBeforeGridStateSave: function (grid, state) {
            var newColumnNames = this._getColumnNamesFromState(state);

            if (!_.isEmpty(newColumnNames)) {
                this._setColumnNames(newColumnNames);

                if (this._userHasPermissionsToEditPanelSettings()) {
                    this.updateSettingsValues({
                        settings: {
                            columnNames: newColumnNames.join(',')
                        }
                    });
                }
            }
        },

        _getValidSorters: function (sorters) {
            return _.filter(sorters, function (sorter) {
                return _.any(this.models, function (model) {
                    var field = model.getField(sorter.property);
                    return field && field.sortable;
                });
            }, this);
        },

        _userHasPermissionsToEditPanelSettings: function () {
            return this.owner.dashboard.arePanelSettingsEditable;
        },

        _getColumnNamesFromState: function (state) {
            return _(state && state.columns).map(function (newColumn) {
                return _.isObject(newColumn) ? newColumn.dataIndex : newColumn;
            }).compact().value();
        }
    });
})();
<|endoftext|>


<commit_before>'use strict';
var ngMeteorCollections = angular.module('ngMeteor.collections', []);

ngMeteorCollections.factory('$collection', ['$q', 'HashKeyCopier',
  function ($q, HashKeyCopier) {
    return function (collection, selector, options) {
      if (!selector) selector = {};
      if (!(collection instanceof Meteor.Collection)) {
        throw new TypeError("The first argument of $collection must be a Meteor.Collection object.");
      }
      return {

        bindOne: function(scope, model, id) {
          Deps.autorun(function(self) {
            scope[model] = collection.findOne(id);
            if (!scope.$$phase) scope.$apply(); // Update bindings in scope.
            scope.$on('$destroy', function () {
              self.stop(); // Stop computation if scope is destroyed.
            });
          });
        },

        bind: function (scope, model, auto) {
          auto = auto || false; // Sets default binding type.
          if (!(typeof auto === 'boolean')) { // Checks if auto is a boolean.
            throw new TypeError("The third argument of bind must be a boolean.");
          }

          Deps.autorun(function (self) {
            var ngCollection = new AngularMeteorCollection(collection, $q, selector, options);

            // Bind collection to model in scope. Transfer $$hashKey based on _id.
            scope[model] = HashKeyCopier.copyHashKeys(scope[model], ngCollection, ["_id"]);

            if (!scope.$$phase) scope.$apply(); // Update bindings in scope.
            scope.$on('$destroy', function () {
              self.stop(); // Stop computation if scope is destroyed.
            });
          });

          if (auto) { // Deep watches the model and performs autobind.
            scope.$watch(model, function (newItems, oldItems) {
              // Remove items that don't exist in the collection anymore.
              angular.forEach(oldItems, function (oldItem) {
                var index = newItems.map(function (item) {
                  return item._id;
                }).indexOf(oldItem._id);
                if (index == -1) { // To here get all objects that pushed or spliced
                  if (oldItem._id) { // This is a check to get only the spliced objects
                    newItems.remove(oldItem._id);
                  }
                }
              });
              newItems.save(); // Saves all items.
            }, auto);
          }
        }

      };
    }
  }
]);

var AngularMeteorCollection = function (collection, $q, selector, options) {
  var self = collection.find(selector, options).fetch();

  self.__proto__ = AngularMeteorCollection.prototype;
  self.__proto__.$q = $q;
  self.$$collection = collection;

  return self;
};

AngularMeteorCollection.prototype = []; // Allows inheritance of native Array methods.

AngularMeteorCollection.prototype.save = function save(docs) {
  var self = this,
    collection = self.$$collection,
    $q = self.$q,
    promises = []; // To store all promises.

  /*
   * The upsertObject function will either update an object if the _id exists
   * or insert an object if the _id is not set in the collection.
   * Returns a promise.
   */
  function upsertObject(item, $q) {
    var deferred = $q.defer();

    item = angular.copy(item);
    delete item.$$hashKey;
    for (var property in item) {
      delete property.$$hashKey;
    }

    if (item._id) { // Performs an update if the _id property is set.
      var item_id = item._id; // Store the _id in temporary variable
      delete item._id; // Remove the _id property so that it can be $set using update.
      var objectId = (item_id._str) ? new Meteor.Collection.ObjectID(item_id._str) : item_id;
      collection.update(objectId, {$set: item}, function (error) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: objectId, action: "updated"});
        }
      });
    } else { // Performs an insert if the _id property isn't set.
      collection.insert(item, function (error, result) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: result, action: "inserted"});
        }
      });
    }

    return deferred.promise;
  }

  /*
   * How to update the collection depending on the 'docs' argument passed.
   */
  if (docs) { // Checks if a 'docs' argument was passed.
    if (angular.isArray(docs)) { // If an array of objects were passed.
      angular.forEach(docs, function (doc) {
        this.push(upsertObject(doc, $q));
      }, promises);
    } else { // If a single object was passed.
      promises.push(upsertObject(docs, $q));
    }
  } else { // If no 'docs' argument was passed, save the entire collection.
    angular.forEach(self, function (doc) {
      this.push(upsertObject(doc, $q));
    }, promises);
  }

  return $q.all(promises); // Returns all promises when they're resolved.
};

AngularMeteorCollection.prototype.remove = function remove(keys) {
  var self = this,
    collection = self.$$collection,
    $q = self.$q,
    promises = []; // To store all promises.

  /*
   * The removeObject function will delete an object with the _id property
   * equal to the specified key.
   * Returns a promise.
   */
  function removeObject(key, $q) {
    var deferred = $q.defer();

    if (key) { // Checks if 'key' argument is set.
      if(key._id) {
        key = key._id;
      }
      var objectId = (key._str) ? new Meteor.Collection.ObjectID(key._str) : key;
      collection.remove(objectId, function (error) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: objectId, action: "removed"});
        }
      });
    } else {
      deferred.reject("key cannot be null");
    }

    return deferred.promise;
  }

  /*
   * What to remove from collection depending on the 'keys' argument passed.
   */
  if (keys) { // Checks if a 'keys' argument was passed.
    if (angular.isArray(keys)) { // If an array of keys were passed.
      angular.forEach(keys, function (key) {
        this.push(removeObject(key, $q));
      }, promises);
    } else { // If a single key was passed.
      promises.push(removeObject(keys, $q));
    }
  } else { // If no 'keys' argument was passed, save the entire collection.
    angular.forEach(self, function (doc) {
      this.push(removeObject(doc._id, $q));
    }, promises);
  }

  return $q.all(promises); // Returns all promises when they're resolved.
};
<commit_msg>Add autobind to bindOne as well<commit_after>'use strict';
var ngMeteorCollections = angular.module('ngMeteor.collections', []);

ngMeteorCollections.factory('$collection', ['$q', 'HashKeyCopier',
  function ($q, HashKeyCopier) {
    return function (collection, selector, options) {
      if (!selector) selector = {};
      if (!(collection instanceof Meteor.Collection)) {
        throw new TypeError("The first argument of $collection must be a Meteor.Collection object.");
      }
      return {

        bindOne: function(scope, model, id, auto) {
          Deps.autorun(function(self) {
            scope[model] = collection.findOne(id);
            if (!scope.$$phase) scope.$apply(); // Update bindings in scope.
            scope.$on('$destroy', function () {
              self.stop(); // Stop computation if scope is destroyed.
            });
          });

          if (auto) { // Deep watches the model and performs autobind.
            scope.$watch(model, function (newItem, oldItem) {
              if (newItem)
                if (newItem._id)
                  collection.update({_id: newItem._id}, { $set: _.omit(newItem, '_id') });
            }, true);
          }
        },

        bind: function (scope, model, auto) {
          auto = auto || false; // Sets default binding type.
          if (!(typeof auto === 'boolean')) { // Checks if auto is a boolean.
            throw new TypeError("The third argument of bind must be a boolean.");
          }

          Deps.autorun(function (self) {
            var ngCollection = new AngularMeteorCollection(collection, $q, selector, options);

            // Bind collection to model in scope. Transfer $$hashKey based on _id.
            scope[model] = HashKeyCopier.copyHashKeys(scope[model], ngCollection, ["_id"]);

            if (!scope.$$phase) scope.$apply(); // Update bindings in scope.
            scope.$on('$destroy', function () {
              self.stop(); // Stop computation if scope is destroyed.
            });
          });

          if (auto) { // Deep watches the model and performs autobind.
            scope.$watch(model, function (newItems, oldItems) {
              // Remove items that don't exist in the collection anymore.
              angular.forEach(oldItems, function (oldItem) {
                var index = newItems.map(function (item) {
                  return item._id;
                }).indexOf(oldItem._id);
                if (index == -1) { // To here get all objects that pushed or spliced
                  if (oldItem._id) { // This is a check to get only the spliced objects
                    newItems.remove(oldItem._id);
                  }
                }
              });
              newItems.save(); // Saves all items.
            }, auto);
          }
        }

      };
    }
  }
]);

var AngularMeteorCollection = function (collection, $q, selector, options) {
  var self = collection.find(selector, options).fetch();

  self.__proto__ = AngularMeteorCollection.prototype;
  self.__proto__.$q = $q;
  self.$$collection = collection;

  return self;
};

AngularMeteorCollection.prototype = []; // Allows inheritance of native Array methods.

AngularMeteorCollection.prototype.save = function save(docs) {
  var self = this,
    collection = self.$$collection,
    $q = self.$q,
    promises = []; // To store all promises.

  /*
   * The upsertObject function will either update an object if the _id exists
   * or insert an object if the _id is not set in the collection.
   * Returns a promise.
   */
  function upsertObject(item, $q) {
    var deferred = $q.defer();

    item = angular.copy(item);
    delete item.$$hashKey;
    for (var property in item) {
      delete property.$$hashKey;
    }

    if (item._id) { // Performs an update if the _id property is set.
      var item_id = item._id; // Store the _id in temporary variable
      delete item._id; // Remove the _id property so that it can be $set using update.
      var objectId = (item_id._str) ? new Meteor.Collection.ObjectID(item_id._str) : item_id;
      collection.update(objectId, {$set: item}, function (error) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: objectId, action: "updated"});
        }
      });
    } else { // Performs an insert if the _id property isn't set.
      collection.insert(item, function (error, result) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: result, action: "inserted"});
        }
      });
    }

    return deferred.promise;
  }

  /*
   * How to update the collection depending on the 'docs' argument passed.
   */
  if (docs) { // Checks if a 'docs' argument was passed.
    if (angular.isArray(docs)) { // If an array of objects were passed.
      angular.forEach(docs, function (doc) {
        this.push(upsertObject(doc, $q));
      }, promises);
    } else { // If a single object was passed.
      promises.push(upsertObject(docs, $q));
    }
  } else { // If no 'docs' argument was passed, save the entire collection.
    angular.forEach(self, function (doc) {
      this.push(upsertObject(doc, $q));
    }, promises);
  }

  return $q.all(promises); // Returns all promises when they're resolved.
};

AngularMeteorCollection.prototype.remove = function remove(keys) {
  var self = this,
    collection = self.$$collection,
    $q = self.$q,
    promises = []; // To store all promises.

  /*
   * The removeObject function will delete an object with the _id property
   * equal to the specified key.
   * Returns a promise.
   */
  function removeObject(key, $q) {
    var deferred = $q.defer();

    if (key) { // Checks if 'key' argument is set.
      if(key._id) {
        key = key._id;
      }
      var objectId = (key._str) ? new Meteor.Collection.ObjectID(key._str) : key;
      collection.remove(objectId, function (error) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: objectId, action: "removed"});
        }
      });
    } else {
      deferred.reject("key cannot be null");
    }

    return deferred.promise;
  }

  /*
   * What to remove from collection depending on the 'keys' argument passed.
   */
  if (keys) { // Checks if a 'keys' argument was passed.
    if (angular.isArray(keys)) { // If an array of keys were passed.
      angular.forEach(keys, function (key) {
        this.push(removeObject(key, $q));
      }, promises);
    } else { // If a single key was passed.
      promises.push(removeObject(keys, $q));
    }
  } else { // If no 'keys' argument was passed, save the entire collection.
    angular.forEach(self, function (doc) {
      this.push(removeObject(doc._id, $q));
    }, promises);
  }

  return $q.all(promises); // Returns all promises when they're resolved.
};
<|endoftext|>


<commit_before>/* eslint-env mocha */

// Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must
import expect from 'must'

// ===================================================================

import {getConnection, getConfig, getOneHost, waitObjectState} from './util'
import {map, assign} from 'lodash'
import eventToPromise from 'event-to-promise'

// ===================================================================

describe('disk', function () {
  let xo
  let diskIds = []
  let serverId

  before(async function () {
    this.timeout(30e3)
    xo = await getConnection()

    const config = await getConfig()

    serverId = await xo.call('server.add', assign(
      {autoConnect: false}, config.xenServer1
    ))
    await xo.call('server.connect', {id: serverId})
    await eventToPromise(xo.objects, 'finish')
  })

  afterEach(async function () {
    await Promise.all(map(
      diskIds,
      diskId => xo.call('vdi.delete', {id: diskId})
    ))
    diskIds = []
  })

  after(async function () {
    await xo.call('server.remove', {
      id: serverId
    })
  })

  async function createDisk (params) {
    const diskId = await xo.call('disk.create', params)
    diskIds.push(diskId)
    return diskId
  }

  async function getSrId () {
    const host = getOneHost(xo)
    const pool = await xo.getOrWaitObject(host.$poolId)
    return pool.default_SR
  }

// ===================================================================

  describe('.create()', function () {
    this.timeout(30e3)
    it('create a new disk on a SR', async function () {
      const srId = await getSrId()
      const diskId = await createDisk({
        name: 'diskTest',
        size: '1GB',
        sr: srId
      })

      await Promise.all([
        waitObjectState(xo, diskId, disk => {
          expect(disk.type).to.be.equal('VDI')
          expect(disk.name_label).to.be.equal('diskTest')
          // TODO: should not test an exact value but around 10%
          expect(disk.size).to.be.equal(1000341504)
          expect(disk.$SR).to.be.equal(srId)
        }),
        waitObjectState(xo, srId, sr => {
          expect(sr.VDIs).include(diskId)
        })
      ])
    })
  })

  // -------------------------------------------------------------------

  describe('.delete()', function () {
    it('deletes a disk', async function () {
      const srId = await getSrId()
      const diskId = await createDisk({
        name: 'diskTest',
        size: '1MB',
        sr: srId
      })

      await Promise.all([
        xo.call('vdi.delete', {id: diskId}),
        waitObjectState(xo, diskId, disk => {
          expect(disk).to.be.undefined()
        }),
        waitObjectState(xo, srId, sr => {
          expect(sr.VDIs).not.include(diskId)
        })
      ])
      diskIds = []
    })
  })

  // ---------------------------------------------------------------------

  describe('.set()', function () {
    it('set the name of the disk', async function () {
      const srId = await getSrId()
      const diskId = await createDisk({
        name: 'disk1',
        size: '1MB',
        sr: srId
      })
      await xo.call('vdi.set', {
        id: diskId,
        name_label: 'disk2'
      })

      await waitObjectState(xo, diskId, disk => {
        expect(disk.name_label).to.be.equal('disk2')
      })
    })

    it('set the description of the disk', async function () {
      const srId = await getSrId()
      const diskId = await createDisk({
        name: 'diskTest',
        size: '1MB',
        sr: srId
      })
      await xo.call('vdi.set', {
        id: diskId,
        name_description: 'description'
      })

      await waitObjectState(xo, diskId, disk => {
        expect(disk.name_description).to.be.equal('description')
      })
    })

    it('set the size of the disk', async function () {
      const srId = await getSrId()
      const diskId = await createDisk({
        name: 'diskTest',
        size: '1MB',
        sr: srId
      })
      console.log(diskId)
      const disk = await xo.getOrWaitObject(diskId)
      console.log(disk)
      await xo.call('vdi.set', {
        id: diskId,
        size: '5MB'
      })

      await waitObjectState(xo, diskId, disk => {
        console.log(disk)
        expect(disk.size).to.be.equal(6291456)
      })
    })
  })

  // -------------------------------------------------------------------

  describe('.migrate()', function () {
    it('')
  })
})
<commit_msg>Reorganization disk.spec code<commit_after>/* eslint-env mocha */

// Doc: https://github.com/moll/js-must/blob/master/doc/API.md#must
import expect from 'must'

// ===================================================================

import {getConnection, getConfig, getOneHost, waitObjectState} from './util'
import {map, assign} from 'lodash'
import eventToPromise from 'event-to-promise'

// ===================================================================

describe('disk', function () {
  let diskId
  let diskIds = []
  let serverId
  let srId
  let xo

  // -----------------------------------------------------------------

  before(async function () {
    this.timeout(10e3)
    xo = await getConnection()

    const config = await getConfig()
    serverId = await xo.call('server.add', assign(
      {autoConnect: false}, config.xenServer1
    ))
    await xo.call('server.connect', {id: serverId})
    await eventToPromise(xo.objects, 'finish')
  })

  // -----------------------------------------------------------------

  afterEach(async function () {
    await Promise.all(map(
      diskIds,
      diskId => xo.call('vdi.delete', {id: diskId})
    ))
    diskIds = []
  })

  // -----------------------------------------------------------------

  after(async function () {
    await xo.call('server.remove', {id: serverId})
  })

  // -----------------------------------------------------------------

  async function createDisk (params) {
    diskId = await xo.call('disk.create', params)
    diskIds.push(diskId)
  }

  async function getSrId () {
    const host = getOneHost(xo)
    const pool = await xo.getOrWaitObject(host.$poolId)
    return pool.default_SR
  }

// ===================================================================

  describe('.create()', function () {
    it('create a new disk on a SR', async function () {
      srId = await getSrId()
      await createDisk({
        name: 'diskTest',
        size: '1GB',
        sr: srId
      })

      await Promise.all([
        waitObjectState(xo, diskId, disk => {
          expect(disk.type).to.be.equal('VDI')
          expect(disk.name_label).to.be.equal('diskTest')
          // TODO: should not test an exact value but around 10%
          expect(disk.size).to.be.equal(1000341504)
          expect(disk.$SR).to.be.equal(srId)
        }),
        waitObjectState(xo, srId, sr => {
          expect(sr.VDIs).include(diskId)
        })
      ])
    })
  })

  // -------------------------------------------------------------------

  describe('.delete()', function () {
    beforeEach(async function () {
      srId = await getSrId()
      await createDisk({
        name: 'diskTest',
        size: '1GB',
        sr: srId
      })
    })

    it('deletes a disk', async function () {
      await Promise.all([
        xo.call('vdi.delete', {id: diskId}),
        waitObjectState(xo, diskId, disk => {
          expect(disk).to.be.undefined()
        }),
        waitObjectState(xo, srId, sr => {
          expect(sr.VDIs).not.include(diskId)
        })
      ])
      diskIds = []
    })
  })

  // ---------------------------------------------------------------------

  describe('.set()', function () {
    beforeEach(async function () {
      srId = await getSrId()
      await createDisk({
        name: 'diskTest',
        size: '1GB',
        sr: srId
      })
    })

    it('set the name of the disk', async function () {
      await xo.call('vdi.set', {
        id: diskId,
        name_label: 'disk2'
      })

      await waitObjectState(xo, diskId, disk => {
        expect(disk.name_label).to.be.equal('disk2')
      })
    })

    it('set the description of the disk', async function () {
      await xo.call('vdi.set', {
        id: diskId,
        name_description: 'description'
      })

      await waitObjectState(xo, diskId, disk => {
        expect(disk.name_description).to.be.equal('description')
      })
    })

    it.skip('set the size of the disk', async function () {
      console.log(diskId)
      const disk = await xo.getOrWaitObject(diskId)
      console.log(disk)
      await xo.call('vdi.set', {
        id: diskId,
        size: '5MB'
      })

      await waitObjectState(xo, diskId, disk => {
        console.log(disk)
        expect(disk.size).to.be.equal(6291456)
      })
    })
  })

  // -------------------------------------------------------------------

  describe('.migrate()', function () {
    beforeEach(async function () {
      srId = await getSrId()
      await createDisk({
        name: 'diskTest',
        size: '1GB',
        sr: srId
      })
    })
    it('')
  })
})
<|endoftext|>


<commit_before>(function() {

// Please leave quotes around keys! They're needed for Space Heater.
var routes = window.routes = [
    {'pattern': '^/stats/?$', 'view_name': 'homepage'},
    {'pattern': '^/$', 'view_name': 'homepage'},

    {'pattern': '^/stats/apps\-added\-by\-payment/$', 'view_name': 'apps_added'},
    {'pattern': '^/stats/apps\-added\-by\-payment$', 'view_name': 'apps_added'},
    {'pattern': '^/stats/total\-developers/$', 'view_name': 'total_developers'},
    {'pattern': '^/stats/total\-developers$', 'view_name': 'total_developers'},
    {'pattern': '^/stats/total\-visits/$', 'view_name': 'total_visits'},
    {'pattern': '^/stats/total\-visits$', 'view_name': 'total_visits'},
    {'pattern': '^/stats/apps\-installed/$', 'view_name': 'apps_installed'},
    {'pattern': '^/stats/apps\-installed$', 'view_name': 'apps_installed'},
    {'pattern': '^/stats/apps\-installed\-total/$', 'view_name': 'apps_installed_total'},
    {'pattern': '^/stats/apps\-installed\-total$', 'view_name': 'apps_installed_total'},
    {'pattern': '^/stats/apps\-by\-type/$', 'view_name': 'apps_by_type'},
    {'pattern': '^/stats/apps\-by\-type$', 'view_name': 'apps_by_type'},
    {'pattern': '^/stats/apps\-available\-by\-type/$', 'view_name': 'apps_available_by_type'},
    {'pattern': '^/stats/apps\-available\-by\-type$', 'view_name': 'apps_available_by_type'},
    {'pattern': '^/stats/apps\-available\-by\-payment/$', 'view_name': 'apps_available_by_premium'},
    {'pattern': '^/stats/apps\-available\-by\-payment$', 'view_name': 'apps_available_by_premium'},
    {'pattern': '^/stats/gross\-revenue/$', 'view_name': 'gross_revenue'},
    {'pattern': '^/stats/gross\-revenue$', 'view_name': 'gross_revenue'},

    // Per app stats:
    {'pattern': '^/stats/app/([^/<>"\']+)$', 'view_name': 'app_dashboard'},
    {'pattern': '^/stats/app/([^/<>"\']+)/$', 'view_name': 'app_dashboard'},
    {'pattern': '^/stats/app/([^/<>"\']+)/installs/$', 'view_name': 'per_app_installs'},
    {'pattern': '^/stats/app/([^/<>"\']+)/installs$', 'view_name': 'per_app_installs'},
    {'pattern': '^/stats/app/([^/<>"\']+)/visits/$', 'view_name': 'per_app_visits'},
    {'pattern': '^/stats/app/([^/<>"\']+)/visits$', 'view_name': 'per_app_visits'},
    {'pattern': '^/stats/app/([^/<>"\']+)/revenue/$', 'view_name': 'per_app_revenue'},
    {'pattern': '^/stats/app/([^/<>"\']+)/revenue$', 'view_name': 'per_app_revenue'},

    {'pattern': '^/tests$', 'view_name': 'tests'},
    {'pattern': '^/debug$', 'view_name': 'debug'}
];

define(
    'routes',
    routes.map(function(i) {return 'views/' + i.view_name;}),
    function() {
        for (var i = 0; i < routes.length; i++) {
            var route = routes[i];
            var view = require('views/' + route.view_name);
            route.view = view;
        }
        return routes;
    }
);

})();
<commit_msg>real stats URL for public use<commit_after>(function() {

// Please leave quotes around keys! They're needed for Space Heater.
var routes = window.routes = [
    {'pattern': '^/statistics/?$', 'view_name': 'homepage'},
    {'pattern': '^/$', 'view_name': 'homepage'},

    {'pattern': '^/statistics/apps\-added\-by\-payment/$', 'view_name': 'apps_added'},
    {'pattern': '^/statistics/apps\-added\-by\-payment$', 'view_name': 'apps_added'},
    {'pattern': '^/statistics/total\-developers/$', 'view_name': 'total_developers'},
    {'pattern': '^/statistics/total\-developers$', 'view_name': 'total_developers'},
    {'pattern': '^/statistics/total\-visits/$', 'view_name': 'total_visits'},
    {'pattern': '^/statistics/total\-visits$', 'view_name': 'total_visits'},
    {'pattern': '^/statistics/apps\-installed/$', 'view_name': 'apps_installed'},
    {'pattern': '^/statistics/apps\-installed$', 'view_name': 'apps_installed'},
    {'pattern': '^/statistics/apps\-installed\-total/$', 'view_name': 'apps_installed_total'},
    {'pattern': '^/statistics/apps\-installed\-total$', 'view_name': 'apps_installed_total'},
    {'pattern': '^/statistics/apps\-by\-type/$', 'view_name': 'apps_by_type'},
    {'pattern': '^/statistics/apps\-by\-type$', 'view_name': 'apps_by_type'},
    {'pattern': '^/statistics/apps\-available\-by\-type/$', 'view_name': 'apps_available_by_type'},
    {'pattern': '^/statistics/apps\-available\-by\-type$', 'view_name': 'apps_available_by_type'},
    {'pattern': '^/statistics/apps\-available\-by\-payment/$', 'view_name': 'apps_available_by_premium'},
    {'pattern': '^/statistics/apps\-available\-by\-payment$', 'view_name': 'apps_available_by_premium'},
    {'pattern': '^/statistics/gross\-revenue/$', 'view_name': 'gross_revenue'},
    {'pattern': '^/statistics/gross\-revenue$', 'view_name': 'gross_revenue'},

    // Per app stats:
    {'pattern': '^/statistics/app/([^/<>"\']+)$', 'view_name': 'app_dashboard'},
    {'pattern': '^/statistics/app/([^/<>"\']+)/$', 'view_name': 'app_dashboard'},
    {'pattern': '^/statistics/app/([^/<>"\']+)/installs/$', 'view_name': 'per_app_installs'},
    {'pattern': '^/statistics/app/([^/<>"\']+)/installs$', 'view_name': 'per_app_installs'},
    {'pattern': '^/statistics/app/([^/<>"\']+)/visits/$', 'view_name': 'per_app_visits'},
    {'pattern': '^/statistics/app/([^/<>"\']+)/visits$', 'view_name': 'per_app_visits'},
    {'pattern': '^/statistics/app/([^/<>"\']+)/revenue/$', 'view_name': 'per_app_revenue'},
    {'pattern': '^/statistics/app/([^/<>"\']+)/revenue$', 'view_name': 'per_app_revenue'},

    {'pattern': '^/tests$', 'view_name': 'tests'},
    {'pattern': '^/debug$', 'view_name': 'debug'}
];

define(
    'routes',
    routes.map(function(i) {return 'views/' + i.view_name;}),
    function() {
        for (var i = 0; i < routes.length; i++) {
            var route = routes[i];
            var view = require('views/' + route.view_name);
            route.view = view;
        }
        return routes;
    }
);

})();
<|endoftext|>