<commit_before>'use strict';

const core = require('./core.js');
const fd = require('./field.js');
const serv = require('./../services/services.js');
const servWeather = require('./../services/weatherService.js');
const db = require('./../AslanDBConnector.js');
/*
*   Fonction qui traite les réponses de type bonjour
*/
function processingGrettings(information, response) {
	if(information != undefined && response != undefined && information.first_name != undefined) {
		var fields = fd.extractFields(response.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{prenom}") != -1) {
			var answer = fd.replaceField(response.result.fulfillment.speech, "{prenom}", information.first_name);
			core.prepareMessage(answer);
		}
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function processingHour(response) {
	if(response != undefined && response.result != undefined && response.result.parameters != undefined
		&& response.result.parameters.ville != undefined) {
		var fields = fd.extractFields(response.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{heure}") != -1) {
			console.log(response.result.parameters.ville);
			serv.getTimeAt(response.result.parameters.ville, function(hour) {
				var answer = fd.replaceField(response.result.fulfillment.speech, "{heure}",hour);
				answer = fd.replaceField(answer,
					"{\"ville\":[\"" + response.result.parameters.ville + "\"]}", response.result.parameters.ville);
				core.prepareMessage(answer);
			});
		}
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function processingFountain(response) {
	if(response != undefined && response.result != undefined && response.result.parameters != undefined
		&& response.result.parameters.location != undefined) {
		serv.nearestFontaines(response.result.parameters.location, 20, function(foutains) {
			message["attachment"] = foutains;
			core.prepareMessage(message);
		});
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function processingCitizen(response) {
	if(response != undefined && response.result != undefined && response.result.parameters != undefined
		&& response.result.parameters.ville != undefined) {
		var fields = fd.extractFields(response.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{heure}") != -1) {
			console.log(response.result.parameters.ville);
			serv.getTimeAt(response.result.parameters.ville, function(hour) {
				var answer = fd.replaceField(response.result.fulfillment.speech, "{heure}",hour);
				answer = fd.replaceField(answer,
					"{\"ville\":[\"" + response.result.parameters.ville + "\"]}", response.result.parameters.ville);
				console.log(answer);
				core.prepareMessage(answer);
			});
		}
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function processingDate(response) {

}

function processingWeather(message, location) {
	if(message != undefined && message.result != undefined && message.result.parameters != undefined) {
		var fields = fd.extractFields(message.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{\"location\":[]}") != -1) {
			message.result.fulfillment.speech = fd.removeFields(message.result.fulfillment.speech);
			db.insertData("conversation", message.result, function(err, data) {
				console.log(err);
			});
			core.askLocation();
		} else if(fields.indexOf("{meteo}") != -1) {
			var coord = "";
			if(location) {
				coord = location.latitude.toString() + "," + location.longitude.toString();
			} else if(message.result.parameters["geo-city"] != undefined){
				coord = message.result.parameters["geo-city"];
			}
			servWeather.JSONP_LocalWeather(coord, formattedDate(), function(response) {
				if(response.data != null && response.data.current_condition != null) {
					console.log(response.data);
					var weather = "";
					if(response.data.current_condition['0'].lang_fr != null)
						weather = response.data.current_condition['0'].lang_fr['0'].value + ", ";
					weather +=  response.data.current_condition['0'].temp_C + "°";
					var answer = fd.replaceField(message.result.fulfillment.speech, "{meteo}", weather);
					core.prepareMessage(answer);
				}
			});
		} else {
			core.prepareMessage(message.result.fulfillment.speech);
		}
	} else {
		core.prepareMessage(message.result.fulfillment.speech);
	}
}

function processingRestaurant(response, location) {
	if(response != undefined && response.result != undefined && response.result.parameters != undefined) {
		var fields = fd.extractFields(response.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{\"location\":[]}") != -1) {
			response.result.fulfillment.speech = fd.removeFields(response.result.fulfillment.speech);
			db.insertData("conversation", {sessionId: response.sessionId, metadata: response.result.metadata, fulfillment: response.result.fulfillment}, function(err, data) {
				console.log(err);
			});
			core.askLocation();
		} else if (fields.indexOf("{restaurants}") != -1 && location != null){
			console.log("on appelle le service")
			core.prepareMessage(response);
			/*serv.getTimeAt(response.result.parameters.ville, function(hour) {
				var answer = fd.replaceField(response.result.fulfillment.speech, "{heure}",hour);
				answer = fd.replaceField(answer,
					"{\"ville\":[\"" + response.result.parameters.ville + "\"]}", response.result.parameters.ville);
				console.log(answer);
				core.prepareMessage(answer);
			});*/
		} else {
			core.prepareMessage(response.result.fulfillment.speech);
		}
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function formattedDate(d = new Date) {
  let month = String(d.getMonth() + 1);
  let day = String(d.getDate());
  const year = String(d.getFullYear());

  if (month.length < 2) month = '0' + month;
  if (day.length < 2) day = '0' + day;

  return `${year}-${month}-${day}`;
}
exports.processingGrettings = processingGrettings;
exports.processingHour = processingHour;
exports.processingFountain = processingFountain;
exports.processingCitizen = processingCitizen;
exports.processingDate = processingDate;
exports.processingWeather = processingWeather;
exports.processingRestaurant = processingRestaurant;<commit_msg>Fix bug<commit_after>'use strict';

const core = require('./core.js');
const fd = require('./field.js');
const serv = require('./../services/services.js');
const servWeather = require('./../services/weatherService.js');
const db = require('./../AslanDBConnector.js');
/*
*   Fonction qui traite les réponses de type bonjour
*/
function processingGrettings(information, response) {
	if(information != undefined && response != undefined && information.first_name != undefined) {
		var fields = fd.extractFields(response.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{prenom}") != -1) {
			var answer = fd.replaceField(response.result.fulfillment.speech, "{prenom}", information.first_name);
			core.prepareMessage(answer);
		}
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function processingHour(response) {
	if(response != undefined && response.result != undefined && response.result.parameters != undefined
		&& response.result.parameters.ville != undefined) {
		var fields = fd.extractFields(response.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{heure}") != -1) {
			console.log(response.result.parameters.ville);
			serv.getTimeAt(response.result.parameters.ville, function(hour) {
				var answer = fd.replaceField(response.result.fulfillment.speech, "{heure}",hour);
				answer = fd.replaceField(answer,
					"{\"ville\":[\"" + response.result.parameters.ville + "\"]}", response.result.parameters.ville);
				core.prepareMessage(answer);
			});
		}
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function processingFountain(response) {
	if(response != undefined && response.result != undefined && response.result.parameters != undefined
		&& response.result.parameters.location != undefined) {
		serv.nearestFontaines(response.result.parameters.location, 20, function(foutains) {
			message["attachment"] = foutains;
			core.prepareMessage(message);
		});
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function processingCitizen(response) {
	if(response != undefined && response.result != undefined && response.result.parameters != undefined
		&& response.result.parameters.ville != undefined) {
		var fields = fd.extractFields(response.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{heure}") != -1) {
			console.log(response.result.parameters.ville);
			serv.getTimeAt(response.result.parameters.ville, function(hour) {
				var answer = fd.replaceField(response.result.fulfillment.speech, "{heure}",hour);
				answer = fd.replaceField(answer,
					"{\"ville\":[\"" + response.result.parameters.ville + "\"]}", response.result.parameters.ville);
				console.log(answer);
				core.prepareMessage(answer);
			});
		}
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function processingDate(response) {

}

function processingWeather(message, location) {
	if(message != undefined && message.result != undefined && message.result.parameters != undefined) {
		var fields = fd.extractFields(message.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{\"location\":[]}") != -1) {
			message.result.fulfillment.speech = fd.removeFields(message.result.fulfillment.speech);
			db.insertData("conversation", {sessionId: message.sessionId, metadata: message.result.metadata, fulfillment: message.result.fulfillment}, function(err, data) {
				console.log(err);
			});
			core.askLocation();
		} else if(fields.indexOf("{meteo}") != -1) {
			var coord = "";
			if(location) {
				coord = location.latitude.toString() + "," + location.longitude.toString();
			} else if(message.result.parameters["geo-city"] != undefined){
				coord = message.result.parameters["geo-city"];
			}
			servWeather.JSONP_LocalWeather(coord, formattedDate(), function(response) {
				if(response.data != null && response.data.current_condition != null) {
					console.log(response.data);
					var weather = "";
					if(response.data.current_condition['0'].lang_fr != null)
						weather = response.data.current_condition['0'].lang_fr['0'].value + ", ";
					weather +=  response.data.current_condition['0'].temp_C + "°";
					var answer = fd.replaceField(message.result.fulfillment.speech, "{meteo}", weather);
					core.prepareMessage(answer);
				}
			});
		} else {
			core.prepareMessage(message.result.fulfillment.speech);
		}
	} else {
		core.prepareMessage(message.result.fulfillment.speech);
	}
}

function processingRestaurant(response, location) {
	if(response != undefined && response.result != undefined && response.result.parameters != undefined) {
		var fields = fd.extractFields(response.result.fulfillment.speech);
		console.log(fields);
		if(fields.indexOf("{\"location\":[]}") != -1) {
			response.result.fulfillment.speech = fd.removeFields(response.result.fulfillment.speech);
			db.insertData("conversation", {sessionId: response.sessionId, metadata: response.result.metadata, fulfillment: response.result.fulfillment}, function(err, data) {
				console.log(err);
			});
			core.askLocation();
		} else if (fields.indexOf("{restaurants}") != -1 && location != null){
			console.log("on appelle le service")
			core.prepareMessage(response);
			/*serv.getTimeAt(response.result.parameters.ville, function(hour) {
				var answer = fd.replaceField(response.result.fulfillment.speech, "{heure}",hour);
				answer = fd.replaceField(answer,
					"{\"ville\":[\"" + response.result.parameters.ville + "\"]}", response.result.parameters.ville);
				console.log(answer);
				core.prepareMessage(answer);
			});*/
		} else {
			core.prepareMessage(response.result.fulfillment.speech);
		}
	} else {
		core.prepareMessage(response.result.fulfillment.speech);
	}
}

function formattedDate(d = new Date) {
  let month = String(d.getMonth() + 1);
  let day = String(d.getDate());
  const year = String(d.getFullYear());

  if (month.length < 2) month = '0' + month;
  if (day.length < 2) day = '0' + day;

  return `${year}-${month}-${day}`;
}
exports.processingGrettings = processingGrettings;
exports.processingHour = processingHour;
exports.processingFountain = processingFountain;
exports.processingCitizen = processingCitizen;
exports.processingDate = processingDate;
exports.processingWeather = processingWeather;
exports.processingRestaurant = processingRestaurant;<|endoftext|>


<commit_before>const locked = {
  'locked': true
};
const shared = {
  'shared': 'only'
};
const favorited = {
  'only_liked': true
};

export default {
  locked,
  shared,
  favorited
};
<commit_msg>Add privacy filters<commit_after>const locked = {
  'locked': true
};

const shared = {
  'shared': 'only'
};

const favorited = {
  'only_liked': true
};

const publicPrivacy = {
  'privacy': 'public'
};

const linkPrivacy = {
  'privacy': 'link'
};

const passwordPrivacy = {
  'privacy': 'password'
};

const privatePrivacy = {
  'privacy': 'private'
};

export default {
  locked,
  shared,
  favorited,
  publicPrivacy,
  linkPrivacy,
  passwordPrivacy,
  privatePrivacy
};
<|endoftext|>


<commit_before>import { buildUrl, contentUrl } from '../../shared/util/url_factory';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import React, { PureComponent } from 'react';

@graphql(
  gql`
    query ContentAndUserQuery($username: String!, $name: String!) {
      fetchContentHead(username: $username, name: $name) {
        username
        section
        album
        name
        thumb
        title
        comments_count
        comments_updated
      }

      fetchPublicUserDataHead(username: $username) {
        description
        favicon
        google_analytics
        logo
        theme
        title
        username
        viewport
      }
    }
  `,
  {
    options: ({ req }) => {
      // The username is either the first part of the path (e.g. hostname.com/mime/section/album/name)
      // or if we're on a user that has a `hostname` defined then it's implicit in the url
      // (e.g. hostname.com/section/album/name) and we figure it out in the user resolver.
      const splitPath = req.path.split('/');
      const probableContentUsername = splitPath[1];
      const probableContentName = splitPath[splitPath.length - 1];

      return {
        variables: {
          username: decodeURIComponent(probableContentUsername),
          name: decodeURIComponent(probableContentName),
        },
      };
    },
  }
)
class HTMLHead extends PureComponent {
  render() {
    const { appName, assetPathsByType, nonce, publicUrl, req } = this.props;

    // XXX(mime): terrible hack until i figure out what's going on.
    // seems that having the same query twice in a tree while doing SSR will prevent rendering of the first
    // component. in this case, HTMLHead's data will be missing.
    const contentOwner = this.props.data.fetchPublicUserDataHead;
    const content = this.props.data.fetchContentHead;

    let description, favicon, rss, theme, title, username;
    const acct = `acct:${username}@${req.get('host')}`;
    const feedUrl = buildUrl({ pathname: '/api/social/feed', searchParams: { username: contentOwner?.username } });
    const repliesUrl =
      content && buildUrl({ pathname: '/api/social/comments', searchParams: { url: contentUrl(content) } });
    const webmentionUrl = buildUrl({ req, pathname: '/api/social/webmention', searchParams: { q: acct } });
    const repliesAttribs = content && {
      'thr:count': content.comments_count,
      'thr:updated': new Date(content.comments_updated).toISOString(),
    };
    let viewport = 'width=device-width, initial-scale=1, shrink-to-fit=no';

    if (contentOwner) {
      description = contentOwner.description && <meta name="description" content={contentOwner.description} />;
      favicon = contentOwner.favicon;
      rss = <link rel="alternate" type="application/atom+xml" title={title} href={feedUrl} />;
      theme = contentOwner.theme && <link rel="stylesheet" href={contentOwner.theme} />;
      title = contentOwner.title;
      username = contentOwner.username;
      viewport = contentOwner.viewport || viewport;
    }

    if (!theme) {
      theme = <link rel="stylesheet" href="/css/themes/pixel.css" />;
    }

    if (!title) {
      title = appName;
    }

    return (
      <head>
        <meta charSet="utf-8" />
        <link rel="author" href={`${publicUrl}humans.txt`} />
        {contentOwner ? (
          <link rel="author" href={contentUrl({ username: contentOwner.username, section: 'main', name: 'about' })} />
        ) : null}
        <link rel="shortcut icon" href={favicon || `${publicUrl}favicon.ico`} />
        {assetPathsByType['css'].map(path => (
          <link nonce={nonce} rel="stylesheet" key={path} href={path} />
        ))}
        {theme}
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" />
        <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
        <link
          rel="search"
          href={buildUrl({ pathname: '/api/opensearch', searchParams: { username } })}
          type="application/opensearchdescription+xml"
          title={title}
        />
        <link rel="canonical" href={content && contentUrl(content)} />
        {rss}
        <meta name="viewport" content={viewport} />
        <meta name="theme-color" content="#000000" />
        <meta name="generator" content="Hello, world. https://github.com/mimecuvalo/helloworld" />
        {description}
        <OpenGraphMetadata contentOwner={contentOwner} content={content} req={req} />
        <link
          rel="alternate"
          type="application/json+oembed"
          href={buildUrl({ pathname: '/api/social/oembed', searchParams: { url: content && contentUrl(content) } })}
          title={content?.title}
        />
        {content ? <link rel="webmention" href={webmentionUrl} /> : null}
        {content?.comments_count ? (
          <link rel="replies" type="application/atom+xml" href={repliesUrl} {...repliesAttribs} />
        ) : null}
        {/*
          manifest.json provides metadata used when your web app is added to the
          homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
        */}
        <link rel="manifest" href={`${publicUrl}manifest.json`} />
        {/*
          Notice the use of publicUrl in the tags above.
          It will be replaced with the URL of the `public` folder during the build.
          Only files inside the `public` folder can be referenced from the HTML.

          Unlike "/favicon.ico" or "favicon.ico", "${publicUrl}favicon.ico" will
          work correctly both with client-side routing and a non-root public URL.
          Learn how to configure a non-root public URL by running `npm run build`.
        */}
        <title>{(content?.title ? content.title + ' – ' : '') + title}</title>
        {/*
          XXX(mime): Material UI's server-side rendering for CSS doesn't allow for inserting CSS the same way we do
          Apollo's data (see apolloStateFn in HTMLBase). So for now, we just do a string replace, sigh.
          See related hacky code in server/app/app.js
        */}
        <style id="jss-ssr" dangerouslySetInnerHTML={{ __html: `<!--MATERIAL-UI-CSS-SSR-REPLACE-->` }} />
        {contentOwner ? <GoogleAnalytics nonce={nonce} contentOwner={contentOwner} /> : null}
      </head>
    );
  }
}

// This needs to be filled out by the developer to provide content for the site.
// Learn more here: http://ogp.me/
const OpenGraphMetadata = React.memo(function OpenGraphMetadata({ contentOwner, content, req }) {
  let thumb;
  if (content?.thumb) {
    thumb = content.thumb;
    if (!/^https?:/.test(thumb)) {
      thumb = buildUrl({ req, pathname: thumb });
    }
  }

  if (!thumb) {
    thumb = buildUrl({ req, pathname: contentOwner?.logo || contentOwner?.favicon });
  }

  return (
    <>
      <meta property="og:title" content={content?.title} />
      <meta property="og:description" content={contentOwner?.description} />
      <meta property="og:type" content="website" />
      <meta property="og:url" content={content && contentUrl(content, req)} />
      <meta property="og:site_name" content={contentOwner?.title} />
      <meta property="og:image" content={thumb} />
    </>
  );
});

// TODO(mime): meh, lame that this is in the <head> but I don't feel like moving this to HTMLBase where we don't have
// contentOwner currently.
function GoogleAnalytics({ nonce, contentOwner }) {
  return (
    <script
      nonce={nonce}
      async
      dangerouslySetInnerHTML={{
        __html: `
        var _gaq = _gaq || [];
        _gaq.push(['_setAccount', '${contentOwner.google_analytics}']);
        _gaq.push(['_trackPageview']);

        (function() {
          var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
          ga.src = 'https://ssl.google-analytics.com/ga.js';
          var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
        })();
        `,
      }}
    />
  );
}

export default HTMLHead;
<commit_msg>cra: sync up with upstream fork, shrink-to-fit no longer included in meta viewport tag<commit_after>import { buildUrl, contentUrl } from '../../shared/util/url_factory';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import React, { PureComponent } from 'react';

@graphql(
  gql`
    query ContentAndUserQuery($username: String!, $name: String!) {
      fetchContentHead(username: $username, name: $name) {
        username
        section
        album
        name
        thumb
        title
        comments_count
        comments_updated
      }

      fetchPublicUserDataHead(username: $username) {
        description
        favicon
        google_analytics
        logo
        theme
        title
        username
        viewport
      }
    }
  `,
  {
    options: ({ req }) => {
      // The username is either the first part of the path (e.g. hostname.com/mime/section/album/name)
      // or if we're on a user that has a `hostname` defined then it's implicit in the url
      // (e.g. hostname.com/section/album/name) and we figure it out in the user resolver.
      const splitPath = req.path.split('/');
      const probableContentUsername = splitPath[1];
      const probableContentName = splitPath[splitPath.length - 1];

      return {
        variables: {
          username: decodeURIComponent(probableContentUsername),
          name: decodeURIComponent(probableContentName),
        },
      };
    },
  }
)
class HTMLHead extends PureComponent {
  render() {
    const { appName, assetPathsByType, nonce, publicUrl, req } = this.props;

    // XXX(mime): terrible hack until i figure out what's going on.
    // seems that having the same query twice in a tree while doing SSR will prevent rendering of the first
    // component. in this case, HTMLHead's data will be missing.
    const contentOwner = this.props.data.fetchPublicUserDataHead;
    const content = this.props.data.fetchContentHead;

    let description, favicon, rss, theme, title, username;
    const acct = `acct:${username}@${req.get('host')}`;
    const feedUrl = buildUrl({ pathname: '/api/social/feed', searchParams: { username: contentOwner?.username } });
    const repliesUrl =
      content && buildUrl({ pathname: '/api/social/comments', searchParams: { url: contentUrl(content) } });
    const webmentionUrl = buildUrl({ req, pathname: '/api/social/webmention', searchParams: { q: acct } });
    const repliesAttribs = content && {
      'thr:count': content.comments_count,
      'thr:updated': new Date(content.comments_updated).toISOString(),
    };
    let viewport = 'width=device-width, initial-scale=1';

    if (contentOwner) {
      description = contentOwner.description && <meta name="description" content={contentOwner.description} />;
      favicon = contentOwner.favicon;
      rss = <link rel="alternate" type="application/atom+xml" title={title} href={feedUrl} />;
      theme = contentOwner.theme && <link rel="stylesheet" href={contentOwner.theme} />;
      title = contentOwner.title;
      username = contentOwner.username;
      viewport = contentOwner.viewport || viewport;
    }

    if (!theme) {
      theme = <link rel="stylesheet" href="/css/themes/pixel.css" />;
    }

    if (!title) {
      title = appName;
    }

    return (
      <head>
        <meta charSet="utf-8" />
        <link rel="author" href={`${publicUrl}humans.txt`} />
        {contentOwner ? (
          <link rel="author" href={contentUrl({ username: contentOwner.username, section: 'main', name: 'about' })} />
        ) : null}
        <link rel="shortcut icon" href={favicon || `${publicUrl}favicon.ico`} />
        {assetPathsByType['css'].map(path => (
          <link nonce={nonce} rel="stylesheet" key={path} href={path} />
        ))}
        {theme}
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" />
        <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
        <link
          rel="search"
          href={buildUrl({ pathname: '/api/opensearch', searchParams: { username } })}
          type="application/opensearchdescription+xml"
          title={title}
        />
        <link rel="canonical" href={content && contentUrl(content)} />
        {rss}
        <meta name="viewport" content={viewport} />
        <meta name="theme-color" content="#000000" />
        <meta name="generator" content="Hello, world. https://github.com/mimecuvalo/helloworld" />
        {description}
        <OpenGraphMetadata contentOwner={contentOwner} content={content} req={req} />
        <link
          rel="alternate"
          type="application/json+oembed"
          href={buildUrl({ pathname: '/api/social/oembed', searchParams: { url: content && contentUrl(content) } })}
          title={content?.title}
        />
        {content ? <link rel="webmention" href={webmentionUrl} /> : null}
        {content?.comments_count ? (
          <link rel="replies" type="application/atom+xml" href={repliesUrl} {...repliesAttribs} />
        ) : null}
        {/*
          manifest.json provides metadata used when your web app is added to the
          homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
        */}
        <link rel="manifest" href={`${publicUrl}manifest.json`} />
        {/*
          Notice the use of publicUrl in the tags above.
          It will be replaced with the URL of the `public` folder during the build.
          Only files inside the `public` folder can be referenced from the HTML.

          Unlike "/favicon.ico" or "favicon.ico", "${publicUrl}favicon.ico" will
          work correctly both with client-side routing and a non-root public URL.
          Learn how to configure a non-root public URL by running `npm run build`.
        */}
        <title>{(content?.title ? content.title + ' – ' : '') + title}</title>
        {/*
          XXX(mime): Material UI's server-side rendering for CSS doesn't allow for inserting CSS the same way we do
          Apollo's data (see apolloStateFn in HTMLBase). So for now, we just do a string replace, sigh.
          See related hacky code in server/app/app.js
        */}
        <style id="jss-ssr" dangerouslySetInnerHTML={{ __html: `<!--MATERIAL-UI-CSS-SSR-REPLACE-->` }} />
        {contentOwner ? <GoogleAnalytics nonce={nonce} contentOwner={contentOwner} /> : null}
      </head>
    );
  }
}

// This needs to be filled out by the developer to provide content for the site.
// Learn more here: http://ogp.me/
const OpenGraphMetadata = React.memo(function OpenGraphMetadata({ contentOwner, content, req }) {
  let thumb;
  if (content?.thumb) {
    thumb = content.thumb;
    if (!/^https?:/.test(thumb)) {
      thumb = buildUrl({ req, pathname: thumb });
    }
  }

  if (!thumb) {
    thumb = buildUrl({ req, pathname: contentOwner?.logo || contentOwner?.favicon });
  }

  return (
    <>
      <meta property="og:title" content={content?.title} />
      <meta property="og:description" content={contentOwner?.description} />
      <meta property="og:type" content="website" />
      <meta property="og:url" content={content && contentUrl(content, req)} />
      <meta property="og:site_name" content={contentOwner?.title} />
      <meta property="og:image" content={thumb} />
    </>
  );
});

// TODO(mime): meh, lame that this is in the <head> but I don't feel like moving this to HTMLBase where we don't have
// contentOwner currently.
function GoogleAnalytics({ nonce, contentOwner }) {
  return (
    <script
      nonce={nonce}
      async
      dangerouslySetInnerHTML={{
        __html: `
        var _gaq = _gaq || [];
        _gaq.push(['_setAccount', '${contentOwner.google_analytics}']);
        _gaq.push(['_trackPageview']);

        (function() {
          var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
          ga.src = 'https://ssl.google-analytics.com/ga.js';
          var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
        })();
        `,
      }}
    />
  );
}

export default HTMLHead;
<|endoftext|>


<commit_before>'use strict';
//var async = require('async');
//var deepExtend = require('deep-extend');
var log = require('debug')('routes');

module.exports = function (app) {
    var path = require('path');
    var serverPath = path.join(__dirname,'../modules');

    app.get('/widget/get-domain-list', function (req, res) {
        log('Retrieving dwDomains');
        //JReeme sez: setMaxListeners so we don't have to see that ridiculous memory leak warning
        app.models.DwDomain.getDataSource().setMaxListeners(0);

        var dwDomain = app.models.DwDomain;

        dwDomain.find(function (err, domains) {
            var domainItems = [];
            domains.forEach(function (domain) {
                domainItems.push({name:domain.name,description: domain.description});
            });
            var renderPath = serverPath.concat('/domainWidget/main');
            res.render(renderPath, {
                "pageTitle": 'Domain Listing',
                "domainList": domainItems
            });
        });
    });

    app.get('/widget/get-url-entities', function (req, res) {
        log('Retrieving Url Entities');
        //JReeme sez: setMaxListeners so we don't have to see that ridiculous memory leak warning
        app.models.DwTrailUrl.getDataSource().setMaxListeners(0);

        var dwTrailUrl = app.models.DwTrailUrl;

        var whereClause={
            "order":"timestamp DESC",
            "where":{"url":req.query.trailUrl},
            "include":[{"relation":"urlExtractions","scope":{"where": {"extractorTypes": {"nin":["text"]}}, "order":"occurrences DESC"}}]
        };

        dwTrailUrl.findOne(whereClause,function (err, trailUrl) {
            if(err || !trailUrl || !trailUrl.urlExtractions){
                return;
            }
            trailUrl.urlExtractions(function(err,urlExt){
                var renderPath = serverPath.concat('/extractedEntityWidget/main');
                    res.render(renderPath, {
                        "pageTitle": 'Extracted Entities',
                        "extractedEntities": urlExt
                    });
                }
            )
        });
    });
};

<commit_msg>send empty response if no extracted items<commit_after>'use strict';
//var async = require('async');
//var deepExtend = require('deep-extend');
var log = require('debug')('routes');

module.exports = function (app) {
    var path = require('path');
    var serverPath = path.join(__dirname,'../modules');

    app.get('/widget/get-domain-list', function (req, res) {
        log('Retrieving dwDomains');
        //JReeme sez: setMaxListeners so we don't have to see that ridiculous memory leak warning
        app.models.DwDomain.getDataSource().setMaxListeners(0);

        var dwDomain = app.models.DwDomain;

        dwDomain.find(function (err, domains) {
            var domainItems = [];
            domains.forEach(function (domain) {
                domainItems.push({name:domain.name,description: domain.description});
            });
            var renderPath = serverPath.concat('/domainWidget/main');
            res.render(renderPath, {
                "pageTitle": 'Domain Listing',
                "domainList": domainItems
            });
        });
    });

    app.get('/widget/get-url-entities', function (req, res) {
        log('Retrieving Url Entities');
        //JReeme sez: setMaxListeners so we don't have to see that ridiculous memory leak warning
        app.models.DwTrailUrl.getDataSource().setMaxListeners(0);

        var dwTrailUrl = app.models.DwTrailUrl;

        var whereClause={
            "order":"timestamp DESC",
            "where":{"url":req.query.trailUrl},
            "include":[{"relation":"urlExtractions","scope":{"where": {"extractorTypes": {"nin":["text"]}}, "order":"occurrences DESC"}}]
        };

        dwTrailUrl.findOne(whereClause,function (err, trailUrl) {
            if(err || !trailUrl || !trailUrl.urlExtractions){
                res.render(renderPath,
                    {
                        "pageTitle": 'Extracted Entities',
                        "extractedEntities": {}
                    });
                return;
            }
            trailUrl.urlExtractions(function(err,urlExt){
                var renderPath = serverPath.concat('/extractedEntityWidget/main');
                    res.render(renderPath, {
                        "pageTitle": 'Extracted Entities',
                        "extractedEntities": urlExt
                    });
                }
            )
        });
    });
};

<|endoftext|>


<commit_before>const _ = require('lodash');
const HomebrewModel = require('./homebrew.model.js').model;
const router = require('express').Router();
const zlib = require('zlib');

// const getTopBrews = (cb)=>{
// 	HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) {
// 		cb(brews);
// 	});
// };

const getGoodBrewTitle = (text)=>{
	const titlePos = text.indexOf('# ');
	if(titlePos !== -1){
		const ending = text.indexOf('\n', titlePos);
		return text.substring(titlePos + 2, ending);
	} else {
		return _.find(text.split('\n'), (line)=>{
			return line;
		});
	}
};

router.post('/api', (req, res)=>{

	let authors = [];
	if(req.account) authors = [req.account.username];

	const newHomebrew = new HomebrewModel(_.merge({},
		req.body,
		{ authors: authors }
	));
	if(!newHomebrew.title){
		newHomebrew.title = getGoodBrewTitle(newHomebrew.text);
	}
	newHomebrew.save((err, obj)=>{
		if(err){
			console.error(err, err.toString(), err.stack);
			return res.status(500).send(`Error while creating new brew, ${err.toString()}`);
		}
		return res.json(obj);
	});
});

router.put('/api/update/:id', (req, res)=>{
	HomebrewModel.get({ editId: req.params.id })
		.then((brew)=>{
			brew = _.merge(brew, req.body);
			brew.textBin = zlib.deflateRawSync(req.body.text);		// Compress brew text to binary before saving
			brew.text = '';										// Clear out the non-binary text field so its not saved twice
			brew.updatedAt = new Date();

			if(req.account) brew.authors = _.uniq(_.concat(brew.authors, req.account.username));

			brew.markModified('authors');
			brew.markModified('systems');

			brew.save((err, obj)=>{
				if(err) throw err;
				return res.status(200).send(obj);
			});
		})
		.catch((err)=>{
			console.log(err);
			return res.status(500).send('Error while saving');
		});
});

router.get('/api/remove/:id', (req, res)=>{
	HomebrewModel.find({ editId: req.params.id }, (err, objs)=>{
		if(!objs.length || err) return res.status(404).send('Can not find homebrew with that id');
		const brew = objs[0];

		// Remove current user as author
		if(req.account){
			brew.authors = _.pull(brew.authors, req.account.username);
			brew.markModified('authors');
		}

		// Delete brew if there are no authors left
		if(!brew.authors.length)
			brew.remove((err)=>{
				if(err) return res.status(500).send('Error while removing');
				return res.status(200).send();
			});
		// Otherwise, save the brew with updated author list
		else
			brew.save((err, savedBrew)=>{
				if(err) throw err;
				return res.status(200).send(savedBrew);
			});
	});
});

module.exports = router;

/*

module.exports = function(app){

	app;

	app.get('/api/search', mw.adminOnly, function(req, res){

		var page = req.query.page || 0;
		var count = req.query.count || 20;

		var query = {};
		if(req.query && req.query.id){
			query = {
				"$or" : [{
					editId : req.query.id
				},{
					shareId : req.query.id
				}]
			};
		}

		HomebrewModel.find(query, {
			text : 0 //omit the text
		}, {
			skip: page*count,
			limit: count*1
		}, function(err, objs){
			if(err) console.log(err);
			return res.json({
				page : page,
				count : count,
				total : homebrewTotal,
				brews : objs
			});

		});
	})

	return app;
}
*/
<commit_msg>Make brand new files also save compressed<commit_after>const _ = require('lodash');
const HomebrewModel = require('./homebrew.model.js').model;
const router = require('express').Router();
const zlib = require('zlib');

// const getTopBrews = (cb)=>{
// 	HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) {
// 		cb(brews);
// 	});
// };

const getGoodBrewTitle = (text)=>{
	const titlePos = text.indexOf('# ');
	if(titlePos !== -1){
		const ending = text.indexOf('\n', titlePos);
		return text.substring(titlePos + 2, ending);
	} else {
		return _.find(text.split('\n'), (line)=>{
			return line;
		});
	}
};

router.post('/api', (req, res)=>{

	let authors = [];
	if(req.account) authors = [req.account.username];

	const newHomebrew = new HomebrewModel(_.merge({},
		req.body,
		{ authors: authors }
	));

	if(!newHomebrew.title){
		newHomebrew.title = getGoodBrewTitle(newHomebrew.text);
	}

	newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text);	// Compress brew text to binary before saving
	newHomebrew.text = '';											// Clear out the non-binary text field so its not saved twice

	newHomebrew.save((err, obj)=>{
		if(err){
			console.error(err, err.toString(), err.stack);
			return res.status(500).send(`Error while creating new brew, ${err.toString()}`);
		}
		return res.json(obj);
	});
});

router.put('/api/update/:id', (req, res)=>{
	HomebrewModel.get({ editId: req.params.id })
		.then((brew)=>{
			brew = _.merge(brew, req.body);
			brew.textBin = zlib.deflateRawSync(req.body.text);	// Compress brew text to binary before saving
			brew.text = '';										// Clear out the non-binary text field so its not saved twice
			brew.updatedAt = new Date();

			if(req.account) brew.authors = _.uniq(_.concat(brew.authors, req.account.username));

			brew.markModified('authors');
			brew.markModified('systems');

			brew.save((err, obj)=>{
				if(err) throw err;
				return res.status(200).send(obj);
			});
		})
		.catch((err)=>{
			console.log(err);
			return res.status(500).send('Error while saving');
		});
});

router.get('/api/remove/:id', (req, res)=>{
	HomebrewModel.find({ editId: req.params.id }, (err, objs)=>{
		if(!objs.length || err) return res.status(404).send('Can not find homebrew with that id');
		const brew = objs[0];

		// Remove current user as author
		if(req.account){
			brew.authors = _.pull(brew.authors, req.account.username);
			brew.markModified('authors');
		}

		// Delete brew if there are no authors left
		if(!brew.authors.length)
			brew.remove((err)=>{
				if(err) return res.status(500).send('Error while removing');
				return res.status(200).send();
			});
		// Otherwise, save the brew with updated author list
		else
			brew.save((err, savedBrew)=>{
				if(err) throw err;
				return res.status(200).send(savedBrew);
			});
	});
});

module.exports = router;

/*

module.exports = function(app){

	app;

	app.get('/api/search', mw.adminOnly, function(req, res){

		var page = req.query.page || 0;
		var count = req.query.count || 20;

		var query = {};
		if(req.query && req.query.id){
			query = {
				"$or" : [{
					editId : req.query.id
				},{
					shareId : req.query.id
				}]
			};
		}

		HomebrewModel.find(query, {
			text : 0 //omit the text
		}, {
			skip: page*count,
			limit: count*1
		}, function(err, objs){
			if(err) console.log(err);
			return res.json({
				page : page,
				count : count,
				total : homebrewTotal,
				brews : objs
			});

		});
	})

	return app;
}
*/
<|endoftext|>


<commit_before>
//
// Copyright 2016 Kary Foundation, Inc.
//   Author: Pouya Kary <k@karyfoundation.org>
//

//
// ─── SET CONSOLE REGEX ──────────────────────────────────────────────────────────
//

    function setConsoleRegEx ( html ) {
        document.getElementById( 'ribbon-console-regexp' ).innerHTML = html;
    }

//
// ─── UPDATE CONSOLE TITLE ───────────────────────────────────────────────────────
//

    function updateConsoleTitle ( ) {
        let dirtStatus = ( currentFile.dirty )? ' • Not Saved' : '';
        setConsoleTitle(`${ path.basename( currentFile.path ) }${ dirtStatus }`);
    }

//
// ─── SET CONSOLE TITLE ──────────────────────────────────────────────────────────
//

    function setConsoleTitle ( title ) {
        document.getElementById( 'ribbon-console-title' ).innerHTML = title;
    }

// ────────────────────────────────────────────────────────────────────────────────
<commit_msg>more considerate<commit_after>
//
// Copyright 2016 Kary Foundation, Inc.
//   Author: Pouya Kary <k@karyfoundation.org>
//

//
// ─── SET CONSOLE REGEX ──────────────────────────────────────────────────────────
//

    function setConsoleRegEx ( html ) {
        document.getElementById( 'ribbon-console-regexp' ).innerHTML = html;
    }

//
// ─── UPDATE CONSOLE TITLE ───────────────────────────────────────────────────────
//

    function updateConsoleTitle ( ) {
        let title = ( currentFile.path === defaultEmptyPath )?
            'Untitled.quartet' : path.basename( currentFile.path );
        let dirtStatus = ( currentFile.dirty )?
            ' • Not Saved' : '';
        setConsoleTitle(`${ title }${ dirtStatus }`);
    }

//
// ─── SET CONSOLE TITLE ──────────────────────────────────────────────────────────
//

    function setConsoleTitle ( title ) {
        document.getElementById( 'ribbon-console-title' ).innerHTML = title;
    }

// ────────────────────────────────────────────────────────────────────────────────
<|endoftext|>


<commit_before>const ipfsApi = require('ipfs-api')
const bodyParser = require('body-parser')

const streamToPromise = (stream) => {
  return new Promise((resolve, reject) => {
    let res = ''
    stream.on('data', (chunk) => {
      res += chunk.toString()
    })
    stream.on('error', (err) => {
      reject(err)
    })
    stream.on('end', () => {
      resolve(res)
    })
  })
}

const host = process.env.ipfsHost

const ipfsRpcRoute = (app) => {
  app.use('/api/ipfs/add', bodyParser.json(), async (req, res) => {
    const {body:{text}} = req
    try {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const buffer = Buffer.from(text)
      const hash = await ipfs.files.add(buffer)
      return res.status(200).json(hash)
    } catch (e) {
      return res.status(500).json(e)
    }
  })

  app.use('/api/ipfs/get', bodyParser.json(), async (req, res) => {
    const {body: {hash}} = req
    try {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const stream = await ipfs.cat(hash)
    debugger
      const result = await streamToPromise(stream)
    debugger
      return res.status(200).json(result)
    } catch (e) {
      return res.status(500).json(e)
    }
  })
}

module.exports = ipfsRpcRoute

/*
const ipfsTypeDefs = `
extend type Mutation {
  ipfsAdd (text: String) : String
  ipfsGet(hash: String) : String
}
`
const host = process.env.ipfsHost

const ipfsResolver = {
  Mutation: {
    ipfsAdd: async (root, {text}) => {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const buffer = Buffer.from(text)
      const hash = await ipfs.files.add(buffer)
      return hash
    },
    ipfsGet: async (hash) => {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const stream = await ipfs.cat(hash)
      return streamToPromise(stream)
    }
  }
}

module.exports = {
  ipfsTypeDefs,
  ipfsResolver
}
*/

/*
Meteor.methods({
  'ipfs.add': async (text) => {
    if (Meteor.isServer) {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const buffer = Buffer.from(text)
      const hash = await ipfs.files.add(buffer)
      return hash
    }
  },

  'ipfs.get': async (hash) => {
    if (Meteor.isServer) {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const stream = await ipfs.cat(hash)
      return streamToPromise(stream)
    }
  }
})
*/
<commit_msg>remove debugger statement<commit_after>const ipfsApi = require('ipfs-api')
const bodyParser = require('body-parser')

const streamToPromise = (stream) => {
  return new Promise((resolve, reject) => {
    let res = ''
    stream.on('data', (chunk) => {
      res += chunk.toString()
    })
    stream.on('error', (err) => {
      reject(err)
    })
    stream.on('end', () => {
      resolve(res)
    })
  })
}

const host = process.env.ipfsHost

const ipfsRpcRoute = (app) => {
  app.use('/api/ipfs/add', bodyParser.json(), async (req, res) => {
    const {body:{text}} = req
    try {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const buffer = Buffer.from(text)
      const hash = await ipfs.files.add(buffer)
      return res.status(200).json(hash)
    } catch (e) {
      return res.status(500).json(e)
    }
  })

  app.use('/api/ipfs/get', bodyParser.json(), async (req, res) => {
    const {body: {hash}} = req
    try {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const stream = await ipfs.cat(hash)
      const result = await streamToPromise(stream)
      return res.status(200).json(result)
    } catch (e) {
      return res.status(500).json(e)
    }
  })
}

module.exports = ipfsRpcRoute

/*
const ipfsTypeDefs = `
extend type Mutation {
  ipfsAdd (text: String) : String
  ipfsGet(hash: String) : String
}
`
const host = process.env.ipfsHost

const ipfsResolver = {
  Mutation: {
    ipfsAdd: async (root, {text}) => {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const buffer = Buffer.from(text)
      const hash = await ipfs.files.add(buffer)
      return hash
    },
    ipfsGet: async (hash) => {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const stream = await ipfs.cat(hash)
      return streamToPromise(stream)
    }
  }
}

module.exports = {
  ipfsTypeDefs,
  ipfsResolver
}
*/

/*
Meteor.methods({
  'ipfs.add': async (text) => {
    if (Meteor.isServer) {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const buffer = Buffer.from(text)
      const hash = await ipfs.files.add(buffer)
      return hash
    }
  },

  'ipfs.get': async (hash) => {
    if (Meteor.isServer) {
      const ipfs = ipfsApi(host, '5001', {protocol: 'http'})
      const stream = await ipfs.cat(hash)
      return streamToPromise(stream)
    }
  }
})
*/
<|endoftext|>


<commit_before>module.exports = {
    noteResolution: 16,
    notesPerBar: 4,
    leadNotePattern: [ 1, 3, 5, 8, 10 ],
    leadBaseNote: 72,
    leadDelayMarginPercent: 10,
    maxLeadTones: 9,
    bpm: 120,
    instrumentConfig: {
        lead: {
            volume: 100,
            muted: false
        },
        lead2: {
            volume: 100,
            muted: false
        },
        bass: {
            volume: 100,
            muted: false
        },
        chords: {
            volume: 100,
            muted: false
        },
        kick: {
            volume: 100,
            muted: false,
            note: 60
        },
        snare: {
            volume: 100,
            muted: false,
            note: 62
        },
        hihat: {
            volume: 100,
            muted: false,
            note: 65
        },
        pads: {
            volume: 100,
            muted: false
        }
    },
    chordProgressions: {
        "1-5-6-4": [ [72, 76, 79, 60, 48], [67, 71, 74, 55, 43], [69, 72, 76, 57, 45], [65,69,72, 53, 41] ],
        "1-5-6-3": [ [72, 76, 79, 60, 48], [67, 71, 74, 55, 43], [69, 72, 76, 57, 45], [64, 67, 71, 52, 40] ],
        "6-5-4-5": [ [69, 72, 76, 57, 45], [67, 71, 74, 55, 43], [65,69,72, 53, 41], [67, 71, 74, 55, 43] ],
        "1-6-4-5": [ [72, 76, 79, 60, 48], [69, 72, 76, 57, 45], [65,69,72, 53, 41], [67, 71, 74, 55, 43] ],
        "1-4-6-5": [ [72, 76, 79, 60, 48], [65,69,72, 53, 41], [69, 72, 76, 57, 45], [67, 71, 74, 55, 43] ],
        "1-5-4-6": [ [72, 76, 79, 60, 48], [67, 71, 74, 55, 43], [65,69,72, 53, 41], [69, 72, 76, 57, 45] ],
        "1-5(6)-6-6": [ [72, 76, 79, 60, 48], [67, 71, 76, 55, 43], [69, 72, 76, 57, 45], [69, 72, 76, 57, 45] ],
        "4-1(6)-5-5": [ [65,69,72, 53, 41], [72, 76, 81, 60, 48], [67, 71, 74, 55, 43], [67, 71, 74, 55, 43] ],
        "4-1(6)-2-2": [ [65,69,72, 53, 41], [72, 76, 81, 60, 48], [74, 77, 81, 62, 50], [74, 77, 81, 62, 50] ],
        "DeepHouse": [ [69, 71, 76, 57, 45], [67, 71, 74, 55, 43], [70, 74, 77, 58, 46], [72 , 76 ,79, 60, 48] ]
    },
    chordProgressionsPerElement: 2,
    progressionElements: {
        "verse": [ "kick", "snare", "bass", "lead", "lead2", "pads" /*, "chords"*/],
        "verse-bridge": [ "kick", "snare", "hihat", "bass", "pads", "lead", "lead2"],
        "chorus-buildup": [ "chords", "lead", "lead2" ],
        "chorus": [ "kick", "snare", "hihat", "bass", "chords", "pads", "lead", "lead2" ] //TODO: crash
    },
    songProgression: [ "verse", "verse-bridge", "chorus-buildup", "chorus" ]

};

<commit_msg>Progression chorus only as default<commit_after>module.exports = {
    noteResolution: 16,
    notesPerBar: 4,
    leadNotePattern: [ 1, 3, 5, 8, 10 ],
    leadBaseNote: 72,
    leadDelayMarginPercent: 10,
    maxLeadTones: 9,
    bpm: 120,
    instrumentConfig: {
        lead: {
            volume: 100,
            muted: false
        },
        lead2: {
            volume: 100,
            muted: false
        },
        bass: {
            volume: 100,
            muted: false
        },
        chords: {
            volume: 100,
            muted: false
        },
        kick: {
            volume: 100,
            muted: false,
            note: 60
        },
        snare: {
            volume: 100,
            muted: false,
            note: 62
        },
        hihat: {
            volume: 100,
            muted: false,
            note: 65
        },
        pads: {
            volume: 100,
            muted: false
        }
    },
    chordProgressions: {
        "1-5-6-4": [ [72, 76, 79, 60, 48], [67, 71, 74, 55, 43], [69, 72, 76, 57, 45], [65,69,72, 53, 41] ],
        "1-5-6-3": [ [72, 76, 79, 60, 48], [67, 71, 74, 55, 43], [69, 72, 76, 57, 45], [64, 67, 71, 52, 40] ],
        "6-5-4-5": [ [69, 72, 76, 57, 45], [67, 71, 74, 55, 43], [65,69,72, 53, 41], [67, 71, 74, 55, 43] ],
        "1-6-4-5": [ [72, 76, 79, 60, 48], [69, 72, 76, 57, 45], [65,69,72, 53, 41], [67, 71, 74, 55, 43] ],
        "1-4-6-5": [ [72, 76, 79, 60, 48], [65,69,72, 53, 41], [69, 72, 76, 57, 45], [67, 71, 74, 55, 43] ],
        "1-5-4-6": [ [72, 76, 79, 60, 48], [67, 71, 74, 55, 43], [65,69,72, 53, 41], [69, 72, 76, 57, 45] ],
        "1-5(6)-6-6": [ [72, 76, 79, 60, 48], [67, 71, 76, 55, 43], [69, 72, 76, 57, 45], [69, 72, 76, 57, 45] ],
        "4-1(6)-5-5": [ [65,69,72, 53, 41], [72, 76, 81, 60, 48], [67, 71, 74, 55, 43], [67, 71, 74, 55, 43] ],
        "4-1(6)-2-2": [ [65,69,72, 53, 41], [72, 76, 81, 60, 48], [74, 77, 81, 62, 50], [74, 77, 81, 62, 50] ],
        "DeepHouse": [ [69, 71, 76, 57, 45], [67, 71, 74, 55, 43], [70, 74, 77, 58, 46], [72 , 76 ,79, 60, 48] ]
    },
    chordProgressionsPerElement: 2,
    progressionElements: {
        "verse": [ "kick", "snare", "bass", "lead", "lead2", "pads" /*, "chords"*/],
        "verse-bridge": [ "kick", "snare", "hihat", "bass", "pads", "lead", "lead2"],
        "chorus-buildup": [ "chords", "lead", "lead2" ],
        "chorus": [ "kick", "snare", "hihat", "bass", "chords", "pads", "lead", "lead2" ] //TODO: crash
    },
    songProgression: [ "chorus" ]

};

<|endoftext|>


<commit_before>var path      = require('path')
var assert    = require('assert')
var readFile  = require('./readFile')
var writeFile = require('./writeFile')
var segments  = require('../path/segments')
var eachAsync = require('../async/iterate').map

// THIS NEEDS WORK!!
// options: flatten, preserve [dir structure], noclobber.
function copy (files, dest, cb) {
  files = Array.isArray(files) ? files : [files]

  eachAsync(files, function (file, key, done) {
    readFile(file, function (err, data) {
      assert.ifError(err)

      var filepath = segments.fromFirst(file)
      var destination = path.resolve(dest, filepath)

      writeFile(destination, data, function (err) {
        assert.ifError(err)
        done(null, 'File ' + file + ' copied to ' + [dest, filepath].join('/'))
      })
    })
  }, function (err, res) {
    assert.ifError(err)
    cb(res)
  })
}

function copySync (files, dest) {
  files = Array.isArray(files) ? files : [files]

  files.forEach(function (file) {
    var destFile = path.resolve(dest, segments.fromFirst(file))
    var content = readFile(file)
    writeFile(destFile, content)
  })
}

module.exports = copy
module.exports.sync = copySync
<commit_msg>update file/copy.js<commit_after>var path      = require('path')
var assert    = require('assert')
var readFile  = require('./readFile')
var writeFile = require('./writeFile')
var segments  = require('../path/segments')
var eachAsync = require('../async/iterate').map

// THIS NEEDS WORK!!
// options: flatten, preserve [dir structure], noclobber.
function copy (files, dest, opts, cb) {
  if (typeof opts === 'function') {
    cb = opts
    opts = {flatten: false, noclobber: false}
  }

  files = Array.isArray(files) ? files : [files]

  eachAsync(files, function (file, key, done) {
    readFile(file, function (err, data) {
      assert.ifError(err)

      var method
      var filepath
      var destination

      if (opts.flatten) method = 'last'
      else method = 'fromFirst'

      filepath = segments[method](file)
      destination = path.resolve(dest, filepath)

      writeFile(destination, data, function (err) {
        assert.ifError(err)
        done(null, 'File ' + file + ' copied to ' + [dest, filepath].join('/'))
      })
    })
  }, function (err, res) {
    assert.ifError(err)
    cb(res)
  })
}

function copySync (files, dest) {
  files = Array.isArray(files) ? files : [files]

  files.forEach(function (file) {
    var destFile = path.resolve(dest, segments.fromFirst(file))
    var content = readFile(file)
    writeFile(destFile, content)
  })
}

module.exports = copy
module.exports.sync = copySync
<|endoftext|>


<commit_before>Stories._ensureIndex({
  shortId: 1
}, {
  unique: 1
});

Meteor.publish("exploreStoriesPub", function(filter, category, skip) {

  if (this.userId) { // TODO launch Remove

    return Stories.find({
      published: true
    }, {
      fields: {
        draftStory: 0,
        history: 0
      },
      limit: 200 // initial limit
    });
  } else {
    this.ready()
  }
});

Meteor.publish("readStoryPub", function(userPathSegment, shortId) {
  if (this.userId) { // TODO launch Remove
    return Stories.find({
      userPathSegment: userPathSegment,
      shortId: shortId,
      published: true
    }, {
      fields: {
        draftStory: 0,
        history: 0
      }
    });
  } else {
    this.ready();
  }
});

Meteor.publish("createStoryPub", function(userPathSegment, shortId) {
  return Stories.find({
    userPathSegment: userPathSegment,
    shortId: shortId
  }, {
    fields: {
      history: 0
    }
  });
});

Meteor.publish("contextBlocksPub", function(storyShortId) {
  if(!storyShortId || !this.userId){
    return this.ready();
  }
  return ContextBlocks.find({
    storyShortId: storyShortId,
    authorId: this.userId
  },{
    fields : {
      fullDetails: 0
    },
    limit: 1000
  });
});

Meteor.publish("minimalUsersPub", function(userIds) { // includes user profile and published stories
  if (!userIds || !userIds.length || userIds.length > 1000) {
    return this.ready();
  }
  return Meteor.users.find({_id: {
    $in: userIds
  }}, {
    fields: {
      "profile.profilePicture": 1,
      "username": 1,
      "services.twitter.id": 1
    }
  });
});

Meteor.publish("userProfilePub", function(username) { // includes user profile and published stories

  userCursor = Meteor.users.find({
    username: username.toLowerCase()
  }, {
    fields: {
      "profile" : 1,
      "username" : 1,
      "services.twitter.id": 1
    }
  });

  var user = userCursor.fetch()[0];

  if (!user){
    return this.ready();
  }

  var userFavorites = user.profile.favorites;
  return [
    userCursor,
    Stories.find({
      _id: {
        $in: userFavorites
      },
      published: true
    }, {
      fields : {
        history: 0,
        draftStory: 0
      },
      limit: 100 // initial limit
  })]
});

Meteor.publish("userStoriesPub", function(username) { // only published stories
  // TODO simplify once stories have author username on them
  var user = Meteor.users.findOne({
    username: username.toLowerCase()
  });

  if (!user) {
    return this.ready();
  }

  var userId = user._id;

  return Stories.find({
    authorId: userId,
    published: true
  },{
    fields : {
      history: 0,
      draftStory: 0
    },
    limit: 100 // initial limit
  });
});

Meteor.publish("myStoriesPub", function() {
  if (this.userId) {
    return Stories.find({
      authorId: this.userId
    }, {
      fields: {
        history: 0
      },
      limit: 1000 // initial limit
    });
  } else {
    return this.ready();
  }
});

Meteor.publish("userData", function () {
  if (this.userId) {
    return Meteor.users.find({_id: this.userId},
      {fields: {
        'accessPriority': 1,
        "services.twitter.id": 1
      }});
  } else {
    this.ready();
  }
});

Meteor.publish("tempUsernamePub", function() {
  if (this.userId) {
    return Meteor.users.find(this.userId, {
      fields: {
        'tempUsername': 1
      }
    });
  } else {
    this.ready();
  }
});

// this publishes info one server facts (used on /stats page)
Facts.setUserIdFilter(function (userId) {
  var user = Meteor.users.findOne(userId);
  return user && user.admin;
});
<commit_msg>simplify userStoriesPub<commit_after>Stories._ensureIndex({
  shortId: 1
}, {
  unique: 1
});

Meteor.publish("exploreStoriesPub", function(filter, category, skip) {

  if (this.userId) { // TODO launch Remove

    return Stories.find({
      published: true
    }, {
      fields: {
        draftStory: 0,
        history: 0
      },
      limit: 200 // initial limit
    });
  } else {
    this.ready()
  }
});

Meteor.publish("readStoryPub", function(userPathSegment, shortId) {
  if (this.userId) { // TODO launch Remove
    return Stories.find({
      userPathSegment: userPathSegment,
      shortId: shortId,
      published: true
    }, {
      fields: {
        draftStory: 0,
        history: 0
      }
    });
  } else {
    this.ready();
  }
});

Meteor.publish("createStoryPub", function(userPathSegment, shortId) {
  return Stories.find({
    userPathSegment: userPathSegment,
    shortId: shortId
  }, {
    fields: {
      history: 0
    }
  });
});

Meteor.publish("contextBlocksPub", function(storyShortId) {
  if(!storyShortId || !this.userId){
    return this.ready();
  }
  return ContextBlocks.find({
    storyShortId: storyShortId,
    authorId: this.userId
  },{
    fields : {
      fullDetails: 0
    },
    limit: 1000
  });
});

Meteor.publish("minimalUsersPub", function(userIds) { // includes user profile and published stories
  if (!userIds || !userIds.length || userIds.length > 1000) {
    return this.ready();
  }
  return Meteor.users.find({_id: {
    $in: userIds
  }}, {
    fields: {
      "profile.profilePicture": 1,
      "username": 1,
      "services.twitter.id": 1
    }
  });
});

Meteor.publish("userProfilePub", function(username) { // includes user profile and published stories

  userCursor = Meteor.users.find({
    username: username.toLowerCase()
  }, {
    fields: {
      "profile" : 1,
      "username" : 1,
      "services.twitter.id": 1
    }
  });

  var user = userCursor.fetch()[0];

  if (!user){
    return this.ready();
  }

  var userFavorites = user.profile.favorites;
  return [
    userCursor,
    Stories.find({
      _id: {
        $in: userFavorites
      },
      published: true
    }, {
      fields : {
        history: 0,
        draftStory: 0
      },
      limit: 100 // initial limit
  })]
});

Meteor.publish("userStoriesPub", function(username) { // only published stories
  if (!username) {
    return this.ready();
  }

  return Stories.find({
    authorUsername: username,
    published: true
  },{
    fields : {
      history: 0,
      draftStory: 0
    },
    limit: 100 // initial limit
  });
});

Meteor.publish("myStoriesPub", function() {
  if (this.userId) {
    return Stories.find({
      authorId: this.userId
    }, {
      fields: {
        history: 0
      },
      limit: 1000 // initial limit
    });
  } else {
    return this.ready();
  }
});

Meteor.publish("userData", function () {
  if (this.userId) {
    return Meteor.users.find({_id: this.userId},
      {fields: {
        'accessPriority': 1,
        "services.twitter.id": 1
      }});
  } else {
    this.ready();
  }
});

Meteor.publish("tempUsernamePub", function() {
  if (this.userId) {
    return Meteor.users.find(this.userId, {
      fields: {
        'tempUsername': 1
      }
    });
  } else {
    this.ready();
  }
});

// this publishes info one server facts (used on /stats page)
Facts.setUserIdFilter(function (userId) {
  var user = Meteor.users.findOne(userId);
  return user && user.admin;
});
<|endoftext|>


<commit_before>import express from 'express';
import controller from '../controllers/controller';

const router = express.Router();

// Home page route

// Route for user signup
router.post('/signup', controller.signup);

// Route for signin
router.post('/signin', controller.signin);

// router.use(controller.sessionHandler);

// Route to post group create info
router.post('/group', controller.createGroup);

// Route to add users to group
router.post('/group/:id/user', controller.groups);

// Route to post messages to groups
router.post('/group/:groupId/messages', controller.messages);

// Route to get messages posted to groups
router.get('/group/:groupId/messages', controller.getMessages);

// Route for logout
/* router.get('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      console.log(err);
    } else {
      res.json({ message: 'Logged out' });
    }
  });
});*/

export default router;
<commit_msg>add middleware for route protection<commit_after>import express from 'express';
import controller from '../controllers/controller';

const router = express.Router();

// Route for user signup
router.post('/signup', controller.signup);

// Route for signin
router.post('/signin', controller.signin);

// Middleware to protect routes
router.use(controller.ensureToken);

// Route to post group create info
router.post('/group', controller.createGroup);

// Route to add users to group
router.post('/group/:groupId/user', controller.groups);

// Route to post messages to groups
router.post('/group/:groupId/messages', controller.messages);

// Route to get messages posted to groups
router.get('/group/:groupId/messages', controller.getMessages);

// Route for logout
/* router.get('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      console.log(err);
    } else {
      res.json({ message: 'Logged out' });
    }
  });
});*/

export default router;
<|endoftext|>


<commit_before>const express = require('express');
const router = express.Router();
const path = require('path');
const Users = require('../models/users');
const Jobs = require('../models/jobs');
const JobContent = require('../models/jobContent');

//NOTE: Posting new users is handled by the auth path, see googleAuth.js

//GET all users
router.get('/', (req, res) => {
  Users.find({}, (err, users) => {
    if (err) {
      console.log(`Find all users error: ${err}`);
      res.send(err);
    } else {
      console.log(`Find all users: ${users}`);
      res.send(users);
    }
  });
});

//GET specific user
router.get('/:userid', (req, res) => {
  Users.findById(req.params.userid)
  // the login route provides the primary populate, here is a backup
  .populate('interested inProgress complete jobContent')
  .exec((err, result) => {
    console.log('result:', result)
    if (err) console.log(`Error: ${err}`)
  })
  .then(found => {
    console.log(found)
    res.send(found);
  });
});

//PUT update specific user
//This route is for updating information on the user's profile and will provide data in the body specifying the elements to be updated
router.put('/:userid', (req, res) => {
  let toUpdate = {};
  for (key in req.body) {
    toUpdate[key] = req.body[key];
  }

  Users.findOneAndUpdate(
    { _id: req.params.userid },
    toUpdate,
    { new: true },
    (err, user) => {
      if (err) { console.log(`Error in user PUT: ${err}`); }
      res.send(user);
    }
  );
});

//GET all user's jobs
router.get('/:userid/jobs', (req, res) => {
  Users.findOne({ _id: req.params.userid })
  //may be able to take this populate out later if the populate occurs elsewhere
  .populate('interested inProgress complete jobContent')
  .exec((err, result) => {
    if (err) { console.log(`Error: ${err}`); }
  })
  .then(found => {
    let allJobs = {
      interested: found.interested,
      inProgress: found.inProgress,
      complete: found.complete,
      jobContent: found.jobContent
    };
    res.send(allJobs);
  });
});

//POST add a job for a user
router.post('/:userid/jobs', (req, res) => {
  //we'll utilize this route when a user either adds via the form or clicks on the 'add to interested' button upon searching
  let userId = req.params.userid;
  //importantly, there needs to be a query string given to this endpoint in order to ascertain which of the user's queues this is going into (so, the value MUST be 'interested', 'inProgress', or 'complete')
  let q = req.query.q;

  //second, put the job in the jobs collection
  //TODO: HANDLE THE CASE WHERE THE JOB IS ALREADY IN THE DB
  let job = new Jobs({
    api: req.body.api,
    apiSpecificId: req.body.apiSpecificId,
    title: req.body.title,
    company: req.body.company,
    location: req.body.location,
    link: req.body.link,
    description: req.body.description
  });
  job.save().then(job => {
    //next we've gotta update the user with the job in the correct queue
    let jobId = job._id;
    //create the job content doc that'll be associated with this user and job
    let jobContent = new JobContent({
      user_id: userId,
      job_id: jobId
    });
    jobContent.save().then(content => {
      jobContentId = content._id;
      //toPush has to be separately defined so we can use the query indicating which array the jobId should be pushed into
      let toPush = {};
      //push to the right job array
      toPush[q] = jobId;
      //also wanna push the new jobContent id into that array
      toPush.jobContent = jobContentId;
      Users.findOneAndUpdate(
        { _id: userId },
        { $push: toPush },
        { new: true }
      )
      //return the updated user
      .then(user => {
        res.json(user);
      });
    });
  });
});

//PUT move a job to a new queue or DELETE a job from a queue
router.route('/:userid/jobs/:jobid/:queue')
  .put((req, res) => {
    let queue = req.params.queue;
    let jobId = req.params.jobid;
    let toAdd = {};
    toAdd[queue] = jobId;
    //when we add we want to remove from other lists
    Users.findOneAndUpdate({ _id: req.params.userid },
      { $push: toAdd,
        $pull: { interested: jobId, complete: jobId }
      },
      { new: true })
      .then(user => {
        res.send(user);
      })
      .catch(error => {
        throw error;
      });
  })
  .delete((req, res) => {
    let queue = req.params.queue;
    let jobId = req.params.jobid;
    let toDelete = {};
    toDelete[queue] = jobId;

    JobContent.findOneAndRemove( { user_id: req.params.userid, job_id: jobId },
      (err, content) => {
        Users.findOneAndUpdate( { _id: req.params.userid },
          { $pull: toDelete },
          { new: true },
          (err, user) => {
            res.send(user);
          }
        );
      }
    );
  });

//POST route for job content is not needed because new jobContent is created at the moment the job is saved (see job POST route).  All updates to job content should be PUT requests
router.post('/:userid/jobs/:jobid/content', (req, res) => {

});

//PUT update job content specific to a user's job
router.put('/:userid/jobs/:jobid/content', (req, res) => {
  let userId = req.params.userid;
  let jobId = req.params.jobid;
  //as with the PUT user route, will provide data in the body about the specific information to be updated
  let toUpdate = {};
  for (key in req.body) {
    toUpdate[key] = req.body[key];
  }
  JobContent.findOneAndUpdate({ user_id: userId, job_id: jobId },
    toUpdate,
    { new: true },
    (err, jobContent) => {
      if (err) { console.log(`Error in jobContent PUT: ${err}`); }
      // res.send(jobContent);
      Users.findOne({ _id: userId }, (user) => {
        res.send(user);
      });
    }
  );
});

module.exports = router;
<commit_msg>updates PUT route for moving jobs to accommodate different scenarios<commit_after>const express = require('express');
const router = express.Router();
const path = require('path');
const Users = require('../models/users');
const Jobs = require('../models/jobs');
const JobContent = require('../models/jobContent');

//NOTE: Posting new users is handled by the auth path, see googleAuth.js

//GET all users
router.get('/', (req, res) => {
  Users.find({}, (err, users) => {
    if (err) {
      console.log(`Find all users error: ${err}`);
      res.send(err);
    } else {
      console.log(`Find all users: ${users}`);
      res.send(users);
    }
  });
});

//GET specific user
router.get('/:userid', (req, res) => {
  Users.findById(req.params.userid)
  // the login route provides the primary populate, here is a backup
  .populate('interested inProgress complete jobContent')
  .exec((err, result) => {
    console.log('result:', result)
    if (err) console.log(`Error: ${err}`)
  })
  .then(found => {
    console.log(found)
    res.send(found);
  });
});

//PUT update specific user
//This route is for updating information on the user's profile and will provide data in the body specifying the elements to be updated
router.put('/:userid', (req, res) => {
  let toUpdate = {};
  for (key in req.body) {
    toUpdate[key] = req.body[key];
  }

  Users.findOneAndUpdate(
    { _id: req.params.userid },
    toUpdate,
    { new: true },
    (err, user) => {
      if (err) { console.log(`Error in user PUT: ${err}`); }
      res.send(user);
    }
  );
});

//GET all user's jobs
router.get('/:userid/jobs', (req, res) => {
  Users.findOne({ _id: req.params.userid })
  //may be able to take this populate out later if the populate occurs elsewhere
  .populate('interested inProgress complete jobContent')
  .exec((err, result) => {
    if (err) { console.log(`Error: ${err}`); }
  })
  .then(found => {
    let allJobs = {
      interested: found.interested,
      inProgress: found.inProgress,
      complete: found.complete,
      jobContent: found.jobContent
    };
    res.send(allJobs);
  });
});

//POST add a job for a user
router.post('/:userid/jobs', (req, res) => {
  //we'll utilize this route when a user either adds via the form or clicks on the 'add to interested' button upon searching
  let userId = req.params.userid;
  //importantly, there needs to be a query string given to this endpoint in order to ascertain which of the user's queues this is going into (so, the value MUST be 'interested', 'inProgress', or 'complete')
  let q = req.query.q;

  //second, put the job in the jobs collection
  //TODO: HANDLE THE CASE WHERE THE JOB IS ALREADY IN THE DB
  let job = new Jobs({
    api: req.body.api,
    apiSpecificId: req.body.apiSpecificId,
    title: req.body.title,
    company: req.body.company,
    location: req.body.location,
    link: req.body.link,
    description: req.body.description
  });
  job.save().then(job => {
    //next we've gotta update the user with the job in the correct queue
    let jobId = job._id;
    //create the job content doc that'll be associated with this user and job
    let jobContent = new JobContent({
      user_id: userId,
      job_id: jobId
    });
    jobContent.save().then(content => {
      jobContentId = content._id;
      //toPush has to be separately defined so we can use the query indicating which array the jobId should be pushed into
      let toPush = {};
      //push to the right job array
      toPush[q] = jobId;
      //also wanna push the new jobContent id into that array
      toPush.jobContent = jobContentId;
      Users.findOneAndUpdate(
        { _id: userId },
        { $push: toPush },
        { new: true }
      )
      //return the updated user
      .then(user => {
        res.json(user);
      });
    });
  });
});

//PUT move a job to a new queue or DELETE a job from a queue
router.route('/:userid/jobs/:jobid/:queue')
  .put((req, res) => {
    let queue = req.params.queue;
    let jobId = req.params.jobid;
    let toAdd = {};
    toAdd[queue] = jobId;
    //when we add we want to remove from other lists
    //if queue = inProgress, remove from interested and complete; if queue == complete, remove from interested and inProgress
    let toRemove;
    if (queue === "inProgress") {
      toRemove = {
        interested: jobId,
        complete: jobId
      }
    } else {
      toRemove = {
        interested: jobId,
        inProgress: jobId
      }
    }
    Users.findOneAndUpdate({ _id: req.params.userid },
      { $push: toAdd,
        $pull: toRemove
      },
      { new: true })
      .then(user => {
        res.send(user);
      })
      .catch(error => {
        throw error;
      });
  })
  .delete((req, res) => {
    let queue = req.params.queue;
    let jobId = req.params.jobid;
    let toDelete = {};
    toDelete[queue] = jobId;

    JobContent.findOneAndRemove( { user_id: req.params.userid, job_id: jobId },
      (err, content) => {
        Users.findOneAndUpdate( { _id: req.params.userid },
          { $pull: toDelete },
          { new: true },
          (err, user) => {
            res.send(user);
          }
        );
      }
    );
  });

//POST route for job content is not needed because new jobContent is created at the moment the job is saved (see job POST route).  All updates to job content should be PUT requests
router.post('/:userid/jobs/:jobid/content', (req, res) => {

});

//PUT update job content specific to a user's job
router.put('/:userid/jobs/:jobid/content', (req, res) => {
  let userId = req.params.userid;
  let jobId = req.params.jobid;
  //as with the PUT user route, will provide data in the body about the specific information to be updated
  let toUpdate = {};
  for (key in req.body) {
    toUpdate[key] = req.body[key];
  }
  JobContent.findOneAndUpdate({ user_id: userId, job_id: jobId },
    toUpdate,
    { new: true },
    (err, jobContent) => {
      if (err) { console.log(`Error in jobContent PUT: ${err}`); }
      // res.send(jobContent);
      Users.findOne({ _id: userId }, (user) => {
        res.send(user);
      });
    }
  );
});

module.exports = router;
<|endoftext|>


<commit_before>const config = require("./config").getProperties();
const mozlog = require("./logging").mozlog("watchdog");
const { captureRavenException } = require("./ravenclient");
const db = require("./db");
const { Shot } = require("./servershot");
const util = require("util");
const { URL } = require("url");
const fetch = require("node-fetch");
const Hawk = require("hawk");
const FormData = require("form-data");
const genUuid = require("nodify-uuid");

const isEnabled = config.watchdog.enable;
validateConfiguration();

function validateConfiguration() {
  if (!isEnabled) {
    return;
  }

  let isConfigValid = true;

  if (!config.watchdog.id || !config.watchdog.key || !config.watchdog.algorithm) {
    mozlog.warn("watchdog-invalid-config", {msg: "Watchdog credentials configuration values are missing."});
    isConfigValid = false;
  }

  if (!config.watchdog.submissionUrl) {
    mozlog.warn("watchdog-invalid-config", {msg: "Watchdog submission URL configuration value is missing."});
    isConfigValid = false;
  } else {
    try {
      new URL(config.watchdog.submissionUrl);
    } catch (e) {
      mozlog.warn("watchdog-invalid-config", {msg: "Watchdog submission URL is not a valid URL.", err: e});
      isConfigValid = false;
    }
  }

  if (!isConfigValid) {
    mozlog.info("watchdog-config-exit", {msg: "Watchdog is enabled but not properly configured."});
    process.exit(2);
  }

}

let positiveEmailList;
if (config.watchdog.positiveEmail) {
  positiveEmailList = config.watchdog.positiveEmail.split(";");
}

let intervalSize = config.watchdog.submissionInterval;
if (intervalSize <= 1) {
  intervalSize = 1;
}
let odometer = 0;
function isAtInterval() {
  if (intervalSize === 1) {
    return true;
  }

  const atInterval = odometer === 0;
  odometer = (odometer + 1) % intervalSize;
  return atInterval;
}

function shouldSubmitShot() {
  return isEnabled && isAtInterval();
}

function getSubmissionId() {
  return db.select(
    "SELECT nextval(pg_get_serial_sequence('watchdog_submissions', 'id')) AS new_id",
  ).then(rows => {
    if (rows.length) {
      return rows[0].new_id;
    }
    throw new Error("Unable to retrieve a new Watchdog submission id.");
  });
}

function getSubmission(id) {
  return db.select(
    "SELECT * FROM watchdog_submissions WHERE id = $1", [id]
  ).then(rows => {
    if (rows.length) {
      return rows[0];
    }
    const err =  new Error("Unable to find a Watchdog submission.");
    err.watchdogSubmissionId = String(id);
    throw err;
  });
}

const credentials = {
  id: config.watchdog.id,
  key: config.watchdog.key,
  algorithm: config.watchdog.algorithm
};

exports.submit = function(shot) {
  if (!shouldSubmitShot()) {
    return;
  }

  if (config.watchdog.devOnlyMatchHostname) {
    try {
      const url = new URL(shot.origin);
      if (url.hostname !== config.watchdog.devOnlyMatchHostname) {
        mozlog.debug("watchdog-debug", { msg: `Shot hostname does not match ${config.watchdog.devOnlyMatchHostname}.` });
        return;
      }
    } catch (e) {
      mozlog.debug("watchdog-debug", { msg: `The shot's invalid hostname does not match ${config.watchdog.devOnlyMatchHostname}.` });
      return;
    }
  }

  const imageUrl = shot.getClip(shot.clipNames()[0]).image.url;
  const imageId = imageUrl.split("/").pop();
  const form = new FormData();

  // When this is called the shot has been successfully saved to the database,
  // but the image data is not necessarily on S3 as the save to S3 is not
  // awaited. Awaiting the S3 upload will affect the responsiveness of saving a
  // shot with the addon.
  //
  // We'll give the S3 upload a head start and try a few times to fetch the
  // image data. This is called for every shot saved, so let's keep it simple
  // and minimal.

  const delay = function(waitInMs) {
    return new Promise((resolve) => {
      setTimeout(() => resolve(), waitInMs);
    });
  };

  const getRawBytesPromise = function(timeToWait, retry) {
    return new Promise((resolve, reject) => {
      delay(timeToWait)
      .then(() => Shot.getRawBytesForClip(imageId))
      .then(obj => {
        if (obj === null) {
          const err = new Error("Shot image expired, blocked, or not found.");
          err.imageId = String(imageId);
          reject(err);
        } else {
          resolve(obj);
        }
      })
      .catch(e => {
        if (!retry) {
          reject(e);
        }
        resolve(null);
      });
    });
  };

  getRawBytesPromise(1000, true)
  .then(bytes => bytes || getRawBytesPromise(1000, true))
  .then(bytes => bytes || getRawBytesPromise(1000))
  .then((obj) => {
    form.append("image", obj.data, {
      filename: imageId,
      contentType: obj.contentType
    });

    return getSubmissionId();
  }).then(submissionId => {
    const uuid = util.promisify(genUuid.generate);
    return uuid(genUuid.V_RANDOM).then(nonce => ({submissionId, nonce}));
  }).then(({submissionId, nonce}) => {
    const callbackUrl = `${shot.backend}/watchdog/${submissionId}?nonce=${nonce}`;
    form.append("positive_uri", callbackUrl);
    form.append("negative_uri", callbackUrl);

    if (positiveEmailList) {
      for (const addr of positiveEmailList) {
        form.append("positive_email[]", addr);
      }
    }

    const req = {method: "POST", body: form};
    const authHeader = Hawk.client.header(config.watchdog.submissionUrl, req.method, {credentials});
    req.headers = Object.assign(
      {Accept: "application/json", Authorization: authHeader.header},
      form.getHeaders());
    return fetch(config.watchdog.submissionUrl, req)
      .then(res => {
        if (res.ok) {
          return res.json();
        }

        const err = new Error("Received error response from Watchdog.");
        err.watchdogSubmissionId = String(submissionId);
        err.responseStatus = res.status;
        err.responseStatusText = res.statusText;
        throw err;
      }).then(respJson => ({submissionId, nonce, request_id: respJson.id}));
  }).then(({submissionId, nonce, request_id}) => {
    return db.insert(
      `INSERT INTO watchdog_submissions (id, shot_id, request_id, nonce)
       VALUES ($1, $2, $3, $4)`,
      [submissionId, shot.id, request_id, nonce.toString()]
    );
  }).catch(e => {
    captureRavenException(e);
    mozlog.error("watchdog-failed-submission", {err: e});
  });
};

exports.handleResult = function(req) {
  getSubmission(req.params.submissionId).then(record => {
    if (record.request_id !== req.body.watchdog_id) {
      const err = new Error("Received mismatching Watchdog ID in callback.");
      err.watchdogSubmissionId = String(record.id);
      throw err;
    }
    if (req.query.nonce !== record.nonce) {
      const err = new Error("Received mismatching nonce in a Watchdog callback.");
      err.watchdogSubmissionId = String(record.id);
      throw err;
    }
    if (record.positive_result !== null) {
      const err = new Error("Watchdog submission already has a result.");
      err.watchdogSubmissionId = String(record.id);
      throw err;
    }

    if (req.body.positive) {
      handlePositive(record);
    } else {
      handleNegative(record);
    }
  }).catch(e => {
    captureRavenException(e);
    mozlog.error("watchdog-failed-callback", {err: e});
  });
};

function handleNegative(record) {
  return db.update("UPDATE watchdog_submissions SET positive_result = FALSE WHERE id = $1", [record.id]);
}

function handlePositive(record) {
  return db.transaction(client => {
    return db.queryWithClient(
      client,
      `UPDATE data
       SET expire_time = NULL, deleted = FALSE, block_type = 'watchdog'
       WHERE id = $1`,
      [record.shot_id]
    ).then(() => db.queryWithClient(
      client,
      "UPDATE watchdog_submissions SET positive_result = TRUE WHERE id = $1",
      [record.id]
    ));
  });
}
<commit_msg>Add logs to Watchdog callbacks. (#4768)<commit_after>const config = require("./config").getProperties();
const mozlog = require("./logging").mozlog("watchdog");
const { captureRavenException } = require("./ravenclient");
const db = require("./db");
const { Shot } = require("./servershot");
const util = require("util");
const { URL } = require("url");
const fetch = require("node-fetch");
const Hawk = require("hawk");
const FormData = require("form-data");
const genUuid = require("nodify-uuid");

const isEnabled = config.watchdog.enable;
validateConfiguration();

function validateConfiguration() {
  if (!isEnabled) {
    return;
  }

  let isConfigValid = true;

  if (!config.watchdog.id || !config.watchdog.key || !config.watchdog.algorithm) {
    mozlog.warn("watchdog-invalid-config", {msg: "Watchdog credentials configuration values are missing."});
    isConfigValid = false;
  }

  if (!config.watchdog.submissionUrl) {
    mozlog.warn("watchdog-invalid-config", {msg: "Watchdog submission URL configuration value is missing."});
    isConfigValid = false;
  } else {
    try {
      new URL(config.watchdog.submissionUrl);
    } catch (e) {
      mozlog.warn("watchdog-invalid-config", {msg: "Watchdog submission URL is not a valid URL.", err: e});
      isConfigValid = false;
    }
  }

  if (!isConfigValid) {
    mozlog.info("watchdog-config-exit", {msg: "Watchdog is enabled but not properly configured."});
    process.exit(2);
  }

}

let positiveEmailList;
if (config.watchdog.positiveEmail) {
  positiveEmailList = config.watchdog.positiveEmail.split(";");
}

let intervalSize = config.watchdog.submissionInterval;
if (intervalSize <= 1) {
  intervalSize = 1;
}
let odometer = 0;
function isAtInterval() {
  if (intervalSize === 1) {
    return true;
  }

  const atInterval = odometer === 0;
  odometer = (odometer + 1) % intervalSize;
  return atInterval;
}

function shouldSubmitShot() {
  return isEnabled && isAtInterval();
}

function getSubmissionId() {
  return db.select(
    "SELECT nextval(pg_get_serial_sequence('watchdog_submissions', 'id')) AS new_id",
  ).then(rows => {
    if (rows.length) {
      return rows[0].new_id;
    }
    throw new Error("Unable to retrieve a new Watchdog submission id.");
  });
}

function getSubmission(id) {
  return db.select(
    "SELECT * FROM watchdog_submissions WHERE id = $1", [id]
  ).then(rows => {
    if (rows.length) {
      return rows[0];
    }
    const err =  new Error("Unable to find a Watchdog submission.");
    err.watchdogSubmissionId = String(id);
    throw err;
  });
}

const credentials = {
  id: config.watchdog.id,
  key: config.watchdog.key,
  algorithm: config.watchdog.algorithm
};

exports.submit = function(shot) {
  if (!shouldSubmitShot()) {
    return;
  }

  if (config.watchdog.devOnlyMatchHostname) {
    try {
      const url = new URL(shot.origin);
      if (url.hostname !== config.watchdog.devOnlyMatchHostname) {
        mozlog.debug("watchdog-debug", { msg: `Shot hostname does not match ${config.watchdog.devOnlyMatchHostname}.` });
        return;
      }
    } catch (e) {
      mozlog.debug("watchdog-debug", { msg: `The shot's invalid hostname does not match ${config.watchdog.devOnlyMatchHostname}.` });
      return;
    }
  }

  const imageUrl = shot.getClip(shot.clipNames()[0]).image.url;
  const imageId = imageUrl.split("/").pop();
  const form = new FormData();

  // When this is called the shot has been successfully saved to the database,
  // but the image data is not necessarily on S3 as the save to S3 is not
  // awaited. Awaiting the S3 upload will affect the responsiveness of saving a
  // shot with the addon.
  //
  // We'll give the S3 upload a head start and try a few times to fetch the
  // image data. This is called for every shot saved, so let's keep it simple
  // and minimal.

  const delay = function(waitInMs) {
    return new Promise((resolve) => {
      setTimeout(() => resolve(), waitInMs);
    });
  };

  const getRawBytesPromise = function(timeToWait, retry) {
    return new Promise((resolve, reject) => {
      delay(timeToWait)
      .then(() => Shot.getRawBytesForClip(imageId))
      .then(obj => {
        if (obj === null) {
          const err = new Error("Shot image expired, blocked, or not found.");
          err.imageId = String(imageId);
          reject(err);
        } else {
          resolve(obj);
        }
      })
      .catch(e => {
        if (!retry) {
          reject(e);
        }
        resolve(null);
      });
    });
  };

  getRawBytesPromise(1000, true)
  .then(bytes => bytes || getRawBytesPromise(1000, true))
  .then(bytes => bytes || getRawBytesPromise(1000))
  .then((obj) => {
    form.append("image", obj.data, {
      filename: imageId,
      contentType: obj.contentType
    });

    return getSubmissionId();
  }).then(submissionId => {
    const uuid = util.promisify(genUuid.generate);
    return uuid(genUuid.V_RANDOM).then(nonce => ({submissionId, nonce}));
  }).then(({submissionId, nonce}) => {
    const callbackUrl = `${shot.backend}/watchdog/${submissionId}?nonce=${nonce}`;
    form.append("positive_uri", callbackUrl);
    form.append("negative_uri", callbackUrl);

    if (positiveEmailList) {
      for (const addr of positiveEmailList) {
        form.append("positive_email[]", addr);
      }
    }

    const req = {method: "POST", body: form};
    const authHeader = Hawk.client.header(config.watchdog.submissionUrl, req.method, {credentials});
    req.headers = Object.assign(
      {Accept: "application/json", Authorization: authHeader.header},
      form.getHeaders());
    return fetch(config.watchdog.submissionUrl, req)
      .then(res => {
        if (res.ok) {
          return res.json();
        }

        const err = new Error("Received error response from Watchdog.");
        err.watchdogSubmissionId = String(submissionId);
        err.responseStatus = res.status;
        err.responseStatusText = res.statusText;
        throw err;
      }).then(respJson => ({submissionId, nonce, request_id: respJson.id}));
  }).then(({submissionId, nonce, request_id}) => {
    return db.insert(
      `INSERT INTO watchdog_submissions (id, shot_id, request_id, nonce)
       VALUES ($1, $2, $3, $4)`,
      [submissionId, shot.id, request_id, nonce.toString()]
    );
  }).catch(e => {
    captureRavenException(e);
    mozlog.error("watchdog-failed-submission", {err: e});
  });
};

exports.handleResult = function(req) {
  getSubmission(req.params.submissionId).then(record => {
    if (record.request_id !== req.body.watchdog_id) {
      const err = new Error("Received mismatching Watchdog ID in callback.");
      err.watchdogSubmissionId = String(record.id);
      throw err;
    }
    if (req.query.nonce !== record.nonce) {
      const err = new Error("Received mismatching nonce in a Watchdog callback.");
      err.watchdogSubmissionId = String(record.id);
      throw err;
    }
    if (record.positive_result !== null) {
      const err = new Error("Watchdog submission already has a result.");
      err.watchdogSubmissionId = String(record.id);
      throw err;
    }

    if (req.body.positive) {
      handlePositive(record);
    } else {
      handleNegative(record);
    }
  }).catch(e => {
    captureRavenException(e);
    mozlog.error("watchdog-failed-callback", {err: e});
  });
};

function handleNegative(record) {
  mozlog.debug("watchdog-negative-match", {msg: `Watchdog submission ${record.id} is a negative match.`});
  return db.update("UPDATE watchdog_submissions SET positive_result = FALSE WHERE id = $1", [record.id]);
}

function handlePositive(record) {
  mozlog.info("watchdog-positive-match", {msg: `Watchdog submission ${record.id} is a positive match.`});
  return db.transaction(client => {
    return db.queryWithClient(
      client,
      `UPDATE data
       SET expire_time = NULL, deleted = FALSE, block_type = 'watchdog'
       WHERE id = $1`,
      [record.shot_id]
    ).then(() => db.queryWithClient(
      client,
      "UPDATE watchdog_submissions SET positive_result = TRUE WHERE id = $1",
      [record.id]
    ));
  }).then(() => {
    mozlog.info("watchdog-successful-commit", {msg: `Watchdog positive match ${record.id} successfully committed to the database.`});
  }).catch(err => {
    mozlog.error("watchdog-failed-commit", {err: `Watchdog positive match ${record.id} not committed to the database: ${err}.`});
    throw err;
  });
}
<|endoftext|>