Merge branch 'master' into derek-moore-bug405_email_notifications_for_comments
Conflicts: mediagoblin/db/mongo/migrations.py
This commit is contained in:
commit
94e6052375
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
||||
/lib/
|
||||
/include/
|
||||
/parts/
|
||||
/share/
|
||||
/mediagoblin.egg-info
|
||||
/docs/_build/
|
||||
/docs/build
|
||||
|
23
extlib/video-js/demo.html
Normal file
23
extlib/video-js/demo.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Video.js | HTML5 Video Player</title>
|
||||
|
||||
<link href="http://vjs.zencdn.net/c/video-js.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- video.js must be in the <head> for older IEs to work. -->
|
||||
<script src="http://vjs.zencdn.net/c/video.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" width="640" height="264"
|
||||
poster="http://video-js.zencoder.com/oceans-clip.png"
|
||||
data-setup="{}">
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
|
||||
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
|
||||
</video>
|
||||
|
||||
</body>
|
||||
</html>
|
427
extlib/video-js/video-js.css
Normal file
427
extlib/video-js/video-js.css
Normal file
@ -0,0 +1,427 @@
|
||||
/*
|
||||
VideoJS Default Styles (http://videojs.com)
|
||||
Version 3.1.0
|
||||
*/
|
||||
|
||||
/*
|
||||
REQUIRED STYLES (be careful overriding)
|
||||
================================================================================ */
|
||||
/* When loading the player, the video tag is replaced with a DIV,
|
||||
that will hold the video tag or object tag for other playback methods.
|
||||
The div contains the video playback element (Flash or HTML5) and controls, and sets the width and height of the video.
|
||||
|
||||
** If you want to add some kind of border/padding (e.g. a frame), or special positioning, use another containing element.
|
||||
Otherwise you risk messing up control positioning and full window mode. **
|
||||
*/
|
||||
.video-js {
|
||||
background-color: #000; position: relative; padding: 0;
|
||||
|
||||
/* Start with 10px for base font size so other dimensions can be em based and easily calculable. */
|
||||
font-size: 10px;
|
||||
|
||||
/* Allow poster to be vertially aligned. */
|
||||
vertical-align: middle;
|
||||
/* display: table-cell; */ /*This works in Safari but not Firefox.*/
|
||||
}
|
||||
|
||||
/* Playback technology elements expand to the width/height of the containing div. <video> or <object> */
|
||||
.video-js .vjs-tech { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||
|
||||
/* Fix for Firefox 9 fullscreen (only if it is enabled). Not needed when checking fullScreenEnabled. */
|
||||
.video-js:-moz-full-screen { position: absolute; }
|
||||
|
||||
/* Fullscreen Styles */
|
||||
body.vjs-full-window {
|
||||
padding: 0; margin: 0;
|
||||
height: 100%; overflow-y: auto; /* Fix for IE6 full-window. http://www.cssplay.co.uk/layouts/fixed.html */
|
||||
}
|
||||
.video-js.vjs-fullscreen {
|
||||
position: fixed; overflow: hidden; z-index: 1000; left: 0; top: 0; bottom: 0; right: 0; width: 100% !important; height: 100% !important;
|
||||
_position: absolute; /* IE6 Full-window (underscore hack) */
|
||||
}
|
||||
.video-js:-webkit-full-screen {
|
||||
width: 100% !important; height: 100% !important;
|
||||
}
|
||||
|
||||
/* Poster Styles */
|
||||
.vjs-poster {
|
||||
margin: 0 auto; padding: 0; cursor: pointer;
|
||||
|
||||
/* Scale with the size of the player div. Works when poster is vertically shorter, but stretches when it's less wide. */
|
||||
position: relative; width: 100%; max-height: 100%;
|
||||
}
|
||||
|
||||
/* Subtiles Styles */
|
||||
.video-js .vjs-subtitles { color: #fff; font-size: 20px; text-align: center; position: absolute; bottom: 40px; left: 0; right: 0; }
|
||||
|
||||
/* Fading sytles, used to fade control bar. */
|
||||
.vjs-fade-in {
|
||||
visibility: visible !important; /* Needed to make sure things hide in older browsers too. */
|
||||
opacity: 1 !important;
|
||||
|
||||
-webkit-transition: visibility 0s linear 0s, opacity 0.3s linear;
|
||||
-moz-transition: visibility 0s linear 0s, opacity 0.3s linear;
|
||||
-ms-transition: visibility 0s linear 0s, opacity 0.3s linear;
|
||||
-o-transition: visibility 0s linear 0s, opacity 0.3s linear;
|
||||
transition: visibility 0s linear 0s, opacity 0.3s linear;
|
||||
}
|
||||
.vjs-fade-out {
|
||||
visibility: hidden !important;
|
||||
opacity: 0 !important;
|
||||
|
||||
-webkit-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
|
||||
-moz-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
|
||||
-ms-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
|
||||
-o-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
|
||||
transition: visibility 0s linear 1.5s,opacity 1.5s linear;
|
||||
}
|
||||
|
||||
/* DEFAULT SKIN (override in another file to create new skins)
|
||||
================================================================================
|
||||
Instead of editing this file, I recommend creating your own skin CSS file to be included after this file,
|
||||
so you can upgrade to newer versions easier. You can remove all these styles by removing the 'vjs-default-skin' class from the tag. */
|
||||
|
||||
/* The default control bar. Created by bar.js */
|
||||
.vjs-default-skin .vjs-controls {
|
||||
position: absolute;
|
||||
bottom: 0; /* Distance from the bottom of the box/video. Keep 0. Use height to add more bottom margin. */
|
||||
left: 0; right: 0; /* 100% width of div */
|
||||
margin: 0; padding: 0; /* Controls are absolutely position, so no padding necessary */
|
||||
height: 2.6em; /* Including any margin you want above or below control items */
|
||||
color: #fff; border-top: 1px solid #404040;
|
||||
|
||||
/* CSS Gradient */
|
||||
/* Can use the Ultimate CSS Gradient Generator: http://www.colorzilla.com/gradient-editor/ */
|
||||
background: #242424; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #242424 50%, #1f1f1f 50%, #171717 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(50%,#242424), color-stop(50%,#1f1f1f), color-stop(100%,#171717)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* IE10+ */
|
||||
/* Filter was causing a lot of weird issues in IE. Elements would stop showing up, or other styles would break. */
|
||||
/*filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#242424', endColorstr='#171717',GradientType=0 );*/ /* IE6-9 */
|
||||
background: linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* W3C */
|
||||
|
||||
/* Start hidden and with 0 opacity. Opacity is used to fade in modern browsers. */
|
||||
/* Can't use display block to hide initially because widths of slider handles aren't calculated and avaialbe for positioning correctly. */
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* General styles for individual controls. */
|
||||
.vjs-default-skin .vjs-control {
|
||||
position: relative; float: left;
|
||||
text-align: center; margin: 0; padding: 0;
|
||||
height: 2.6em; width: 2.6em;
|
||||
}
|
||||
|
||||
.vjs-default-skin .vjs-control:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* Hide control text visually, but have it available for screenreaders: h5bp.com/v */
|
||||
.vjs-default-skin .vjs-control-text { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
|
||||
|
||||
|
||||
/* Play/Pause
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-default-skin .vjs-play-control { width: 5em; cursor: pointer !important; }
|
||||
/* Play Icon */
|
||||
.vjs-default-skin.vjs-paused .vjs-play-control div { width: 15px; height: 17px; background: url('video-js.png'); margin: 0.5em auto 0; }
|
||||
.vjs-default-skin.vjs-playing .vjs-play-control div { width: 15px; height: 17px; background: url('video-js.png') -25px 0; margin: 0.5em auto 0; }
|
||||
|
||||
/* Rewind
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-default-skin .vjs-rewind-control { width: 5em; cursor: pointer !important; }
|
||||
.vjs-default-skin .vjs-rewind-control div { width: 19px; height: 16px; background: url('video-js.png'); margin: 0.5em auto 0; }
|
||||
|
||||
/* Volume/Mute
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-default-skin .vjs-mute-control { width: 3.8em; cursor: pointer !important; float: right; }
|
||||
.vjs-default-skin .vjs-mute-control div { width: 22px; height: 16px; background: url('video-js.png') -75px -25px; margin: 0.5em auto 0; }
|
||||
.vjs-default-skin .vjs-mute-control.vjs-vol-0 div { background: url('video-js.png') 0 -25px; }
|
||||
.vjs-default-skin .vjs-mute-control.vjs-vol-1 div { background: url('video-js.png') -25px -25px; }
|
||||
.vjs-default-skin .vjs-mute-control.vjs-vol-2 div { background: url('video-js.png') -50px -25px; }
|
||||
|
||||
|
||||
.vjs-default-skin .vjs-volume-control { width: 5em; float: right; }
|
||||
.vjs-default-skin .vjs-volume-bar {
|
||||
position: relative; width: 5em; height: 0.6em; margin: 1em auto 0; cursor: pointer !important;
|
||||
|
||||
-moz-border-radius: 0.3em; -webkit-border-radius: 0.3em; border-radius: 0.3em;
|
||||
|
||||
background: #666;
|
||||
background: -moz-linear-gradient(top, #333, #666);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#333), to(#666));
|
||||
background: -webkit-linear-gradient(top, #333, #666);
|
||||
background: -o-linear-gradient(top, #333, #666);
|
||||
background: -ms-linear-gradient(top, #333, #666);
|
||||
background: linear-gradient(top, #333, #666);
|
||||
}
|
||||
.vjs-default-skin .vjs-volume-level {
|
||||
position: absolute; top: 0; left: 0; height: 0.6em;
|
||||
|
||||
-moz-border-radius: 0.3em; -webkit-border-radius: 0.3em; border-radius: 0.3em;
|
||||
|
||||
background: #fff;
|
||||
background: -moz-linear-gradient(top, #fff, #ccc);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ccc));
|
||||
background: -webkit-linear-gradient(top, #fff, #ccc);
|
||||
background: -o-linear-gradient(top, #fff, #ccc);
|
||||
background: -ms-linear-gradient(top, #fff, #ccc);
|
||||
background: linear-gradient(top, #fff, #ccc);
|
||||
}
|
||||
.vjs-default-skin .vjs-volume-handle {
|
||||
position: absolute; top: -0.2em; width: 0.8em; height: 0.8em; background: #ccc; left: 0;
|
||||
border: 1px solid #fff;
|
||||
-moz-border-radius: 0.6em; -webkit-border-radius: 0.6em; border-radius: 0.6em;
|
||||
}
|
||||
|
||||
/* Progress
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-default-skin div.vjs-progress-control {
|
||||
position: absolute;
|
||||
left: 4.8em; right: 4.8em; /* Leave room for time displays. */
|
||||
height: 1.0em; width: auto;
|
||||
top: -1.3em; /* Set above the rest of the controls. And leave room for 2px of borders (progress bottom and controls top). */
|
||||
border-bottom: 1px solid #1F1F1F;
|
||||
border-top: 1px solid #222;
|
||||
|
||||
/* CSS Gradient */
|
||||
background: #333;
|
||||
background: -moz-linear-gradient(top, #222, #333);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#222), to(#333));
|
||||
background: -webkit-linear-gradient(top, #222, #333);
|
||||
background: -o-linear-gradient(top, #333, #222);
|
||||
background: -ms-linear-gradient(top, #333, #222);
|
||||
background: linear-gradient(top, #333, #222);
|
||||
|
||||
|
||||
/* 1px top shadow */
|
||||
/* -webkit-box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15); box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15);*/
|
||||
}
|
||||
|
||||
/* Box containing play and load progresses. Also acts as seek scrubber. */
|
||||
.vjs-default-skin .vjs-progress-holder {
|
||||
position: relative; cursor: pointer !important; /*overflow: hidden;*/
|
||||
padding: 0; margin: 0; /* Placement within the progress control item */
|
||||
height: 1.0em;
|
||||
-moz-border-radius: 0.6em; -webkit-border-radius: 0.6em; border-radius: 0.6em;
|
||||
|
||||
/* CSS Gradient */
|
||||
background: #111;
|
||||
background: -moz-linear-gradient(top, #111, #262626);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#111), to(#262626));
|
||||
background: -webkit-linear-gradient(top, #111, #262626);
|
||||
background: -o-linear-gradient(top, #111, #262626);
|
||||
background: -ms-linear-gradient(top, #111, #262626);
|
||||
background: linear-gradient(top, #111, #262626);
|
||||
}
|
||||
.vjs-default-skin .vjs-progress-holder .vjs-play-progress,
|
||||
.vjs-default-skin .vjs-progress-holder .vjs-load-progress { /* Progress Bars */
|
||||
position: absolute; display: block; height: 1.0em; margin: 0; padding: 0;
|
||||
left: 0; top: 0; /*Needed for IE6*/
|
||||
-moz-border-radius: 0.6em; -webkit-border-radius: 0.6em; border-radius: 0.6em;
|
||||
|
||||
/*width: 0;*/
|
||||
}
|
||||
|
||||
.vjs-default-skin .vjs-play-progress {
|
||||
/* CSS Gradient. */
|
||||
background: #fff; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #fff 0%, #d6d6d6 50%, #fff 100%);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%,#fff), color-stop(50%,#d6d6d6), color-stop(100%,#fff));
|
||||
background: -webkit-linear-gradient(top, #fff 0%,#d6d6d6 50%,#fff 100%);
|
||||
background: -o-linear-gradient(top, #fff 0%,#d6d6d6 50%,#fff 100%);
|
||||
background: -ms-linear-gradient(top, #fff 0%,#d6d6d6 50%,#fff 100%);
|
||||
background: linear-gradient(top, #fff 0%,#d6d6d6 50%,#fff 100%);
|
||||
|
||||
background: #efefef;
|
||||
background: -moz-linear-gradient(top, #efefef 0%, #f5f5f5 50%, #dbdbdb 50%, #f1f1f1 100%);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%,#efefef), color-stop(50%,#f5f5f5), color-stop(50%,#dbdbdb), color-stop(100%,#f1f1f1));
|
||||
background: -webkit-linear-gradient(top, #efefef 0%,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%);
|
||||
background: -o-linear-gradient(top, #efefef 0%,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%);
|
||||
background: -ms-linear-gradient(top, #efefef 0%,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#efefef', endColorstr='#f1f1f1',GradientType=0 );
|
||||
background: linear-gradient(top, #efefef 0%,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%);
|
||||
}
|
||||
.vjs-default-skin .vjs-load-progress {
|
||||
opacity: 0.8;
|
||||
|
||||
/* CSS Gradient */
|
||||
background: #666;
|
||||
background: -moz-linear-gradient(top, #666, #333);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#666), to(#333));
|
||||
background: -webkit-linear-gradient(top, #666, #333);
|
||||
background: -o-linear-gradient(top, #666, #333);
|
||||
background: -ms-linear-gradient(top, #666, #333);
|
||||
background: linear-gradient(top, #666, #333);
|
||||
}
|
||||
|
||||
.vjs-default-skin div.vjs-seek-handle {
|
||||
position: absolute;
|
||||
width: 16px; height: 16px; /* Match img pixles */
|
||||
margin-top: -0.3em;
|
||||
left: 0; top: 0; /*Needed for IE6*/
|
||||
|
||||
background: url('video-js.png') 0 -50px;
|
||||
/* CSS Curved Corners. Needed to make shadows curved. */
|
||||
-moz-border-radius: 0.8em; -webkit-border-radius: 0.8em; border-radius: 0.8em;
|
||||
/* CSS Shadows */
|
||||
-webkit-box-shadow: 0 2px 4px 0 #000; -moz-box-shadow: 0 2px 4px 0 #000; box-shadow: 0 2px 4px 0 #000;
|
||||
}
|
||||
/* Time Display
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-default-skin .vjs-time-controls {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 1.0em; width: 4.8em;
|
||||
top: -1.3em;
|
||||
border-bottom: 1px solid #1F1F1F;
|
||||
border-top: 1px solid #222;
|
||||
background-color: #333;
|
||||
|
||||
font-size: 1em; line-height: 1.0em; font-weight: normal; font-family: Helvetica, Arial, sans-serif;
|
||||
|
||||
background: #333;
|
||||
background: -moz-linear-gradient(top, #222, #333);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#222), to(#333));
|
||||
background: -webkit-linear-gradient(top, #222, #333);
|
||||
background: -o-linear-gradient(top, #333, #222);
|
||||
background: -ms-linear-gradient(top, #333, #222);
|
||||
background: linear-gradient(top, #333, #222);
|
||||
|
||||
/* 1px top shadow */
|
||||
/* -webkit-box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15); box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15);*/
|
||||
}
|
||||
|
||||
.vjs-default-skin .vjs-current-time { left: 0; }
|
||||
|
||||
.vjs-default-skin .vjs-duration { right: 0; display: none; }
|
||||
.vjs-default-skin .vjs-remaining-time { right: 0; }
|
||||
|
||||
.vjs-time-divider { display:none; }
|
||||
|
||||
.vjs-default-skin .vjs-time-control { font-size: 1em; line-height: 1; font-weight: normal; font-family: Helvetica, Arial, sans-serif; }
|
||||
.vjs-default-skin .vjs-time-control span { line-height: 25px; /* Centering vertically */ }
|
||||
|
||||
/* Fullscreen
|
||||
-------------------------------------------------------------------------------- */
|
||||
.vjs-secondary-controls { float: right; }
|
||||
|
||||
.vjs-default-skin .vjs-fullscreen-control { width: 3.8em; cursor: pointer !important; float: right; }
|
||||
.vjs-default-skin .vjs-fullscreen-control div { width: 16px; height: 16px; background: url('video-js.png') -50px 0; margin: 0.5em auto 0; }
|
||||
|
||||
.vjs-default-skin.vjs-fullscreen .vjs-fullscreen-control div { background: url('video-js.png') -75px 0; }
|
||||
|
||||
|
||||
/* Big Play Button (at start)
|
||||
---------------------------------------------------------*/
|
||||
.vjs-default-skin .vjs-big-play-button {
|
||||
display: block; /* Start hidden */ z-index: 2;
|
||||
position: absolute; top: 50%; left: 50%; width: 8.0em; height: 8.0em; margin: -43px 0 0 -43px; text-align: center; vertical-align: center; cursor: pointer !important;
|
||||
border: 0.3em solid #fff; opacity: 0.95;
|
||||
-webkit-border-radius: 25px; -moz-border-radius: 25px; border-radius: 25px;
|
||||
|
||||
background: #454545;
|
||||
background: -moz-linear-gradient(top, #454545 0%, #232323 50%, #161616 50%, #3f3f3f 100%);
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%,#454545), color-stop(50%,#232323), color-stop(50%,#161616), color-stop(100%,#3f3f3f));
|
||||
background: -webkit-linear-gradient(top, #454545 0%,#232323 50%,#161616 50%,#3f3f3f 100%);
|
||||
background: -o-linear-gradient(top, #454545 0%,#232323 50%,#161616 50%,#3f3f3f 100%);
|
||||
background: -ms-linear-gradient(top, #454545 0%,#232323 50%,#161616 50%,#3f3f3f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#454545', endColorstr='#3f3f3f',GradientType=0 );
|
||||
background: linear-gradient(top, #454545 0%,#232323 50%,#161616 50%,#3f3f3f 100%);
|
||||
|
||||
/* CSS Shadows */
|
||||
-webkit-box-shadow: 4px 4px 8px #000; -moz-box-shadow: 4px 4px 8px #000; box-shadow: 4px 4px 8px #000;
|
||||
}
|
||||
|
||||
.vjs-default-skin div.vjs-big-play-button:hover {
|
||||
-webkit-box-shadow: 0 0 80px #fff; -moz-box-shadow: 0 0 80px #fff; box-shadow: 0 0 80px #fff;
|
||||
}
|
||||
|
||||
.vjs-default-skin div.vjs-big-play-button span {
|
||||
position: absolute; top: 50%; left: 50%;
|
||||
display: block; width: 35px; height: 42px;
|
||||
margin: -20px 0 0 -15px; /* Using negative margin to center image. */
|
||||
background: url('video-js.png') -100px 0;
|
||||
}
|
||||
|
||||
/* Loading Spinner
|
||||
---------------------------------------------------------*/
|
||||
/* CSS Spinners by Kilian Valkhof - http://kilianvalkhof.com/2010/css-xhtml/css3-loading-spinners-without-images/ */
|
||||
.vjs-loading-spinner {
|
||||
display: none;
|
||||
position: absolute; top: 50%; left: 50%; width: 55px; height: 55px;
|
||||
margin: -28px 0 0 -28px;
|
||||
-webkit-animation-name: rotatethis;
|
||||
-webkit-animation-duration:1s;
|
||||
-webkit-animation-iteration-count:infinite;
|
||||
-webkit-animation-timing-function:linear;
|
||||
-moz-animation-name: rotatethis;
|
||||
-moz-animation-duration:1s;
|
||||
-moz-animation-iteration-count:infinite;
|
||||
-moz-animation-timing-function:linear;
|
||||
}
|
||||
|
||||
@-webkit-keyframes rotatethis {
|
||||
0% {-webkit-transform:scale(0.6) rotate(0deg); }
|
||||
12.5% {-webkit-transform:scale(0.6) rotate(0deg); }
|
||||
12.51% {-webkit-transform:scale(0.6) rotate(45deg); }
|
||||
25% {-webkit-transform:scale(0.6) rotate(45deg); }
|
||||
25.01% {-webkit-transform:scale(0.6) rotate(90deg);}
|
||||
37.5% {-webkit-transform:scale(0.6) rotate(90deg);}
|
||||
37.51% {-webkit-transform:scale(0.6) rotate(135deg);}
|
||||
50% {-webkit-transform:scale(0.6) rotate(135deg);}
|
||||
50.01% {-webkit-transform:scale(0.6) rotate(180deg);}
|
||||
62.5% {-webkit-transform:scale(0.6) rotate(180deg);}
|
||||
62.51% {-webkit-transform:scale(0.6) rotate(225deg);}
|
||||
75% {-webkit-transform:scale(0.6) rotate(225deg);}
|
||||
75.01% {-webkit-transform:scale(0.6) rotate(270deg);}
|
||||
87.5% {-webkit-transform:scale(0.6) rotate(270deg);}
|
||||
87.51% {-webkit-transform:scale(0.6) rotate(315deg);}
|
||||
100% {-webkit-transform:scale(0.6) rotate(315deg);}
|
||||
}
|
||||
|
||||
@-moz-keyframes rotatethis {
|
||||
0% {-moz-transform:scale(0.6) rotate(0deg);}
|
||||
12.5% {-moz-transform:scale(0.6) rotate(0deg);}
|
||||
12.51% {-moz-transform:scale(0.6) rotate(45deg);}
|
||||
25% {-moz-transform:scale(0.6) rotate(45deg);}
|
||||
25.01% {-moz-transform:scale(0.6) rotate(90deg);}
|
||||
37.5% {-moz-transform:scale(0.6) rotate(90deg);}
|
||||
37.51% {-moz-transform:scale(0.6) rotate(135deg);}
|
||||
50% {-moz-transform:scale(0.6) rotate(135deg);}
|
||||
50.01% {-moz-transform:scale(0.6) rotate(180deg);}
|
||||
62.5% {-moz-transform:scale(0.6) rotate(180deg);}
|
||||
62.51% {-moz-transform:scale(0.6) rotate(225deg);}
|
||||
75% {-moz-transform:scale(0.6) rotate(225deg);}
|
||||
75.01% {-moz-transform:scale(0.6) rotate(270deg);}
|
||||
87.5% {-moz-transform:scale(0.6) rotate(270deg);}
|
||||
87.51% {-moz-transform:scale(0.6) rotate(315deg);}
|
||||
100% {-moz-transform:scale(0.6) rotate(315deg);}
|
||||
}
|
||||
/* Each circle */
|
||||
div.vjs-loading-spinner .ball1 { opacity: 0.12; position:absolute; left: 20px; top: 0px; width: 13px; height: 13px; background: #fff;
|
||||
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
|
||||
|
||||
div.vjs-loading-spinner .ball2 { opacity: 0.25; position:absolute; left: 34px; top: 6px; width: 13px; height: 13px; background: #fff;
|
||||
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
|
||||
|
||||
div.vjs-loading-spinner .ball3 { opacity: 0.37; position:absolute; left: 40px; top: 20px; width: 13px; height: 13px; background: #fff;
|
||||
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
|
||||
|
||||
div.vjs-loading-spinner .ball4 { opacity: 0.50; position:absolute; left: 34px; top: 34px; width: 13px; height: 13px; background: #fff;
|
||||
border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 15px; border: 1px solid #ccc; }
|
||||
|
||||
div.vjs-loading-spinner .ball5 { opacity: 0.62; position:absolute; left: 20px; top: 40px; width: 13px; height: 13px; background: #fff;
|
||||
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
|
||||
|
||||
div.vjs-loading-spinner .ball6 { opacity: 0.75; position:absolute; left: 6px; top: 34px; width: 13px; height: 13px; background: #fff;
|
||||
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
|
||||
|
||||
div.vjs-loading-spinner .ball7 { opacity: 0.87; position:absolute; left: 0px; top: 20px; width: 13px; height: 13px; background: #fff;
|
||||
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
|
||||
|
||||
div.vjs-loading-spinner .ball8 { opacity: 1.00; position:absolute; left: 6px; top: 6px; width: 13px; height: 13px; background: #fff;
|
||||
border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; }
|
1
extlib/video-js/video-js.min.css
vendored
Normal file
1
extlib/video-js/video-js.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
extlib/video-js/video-js.png
Normal file
BIN
extlib/video-js/video-js.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
BIN
extlib/video-js/video-js.swf
Normal file
BIN
extlib/video-js/video-js.swf
Normal file
Binary file not shown.
3744
extlib/video-js/video.js
Normal file
3744
extlib/video-js/video.js
Normal file
File diff suppressed because it is too large
Load Diff
21
extlib/video-js/video.min.js
vendored
Normal file
21
extlib/video-js/video.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -120,7 +120,7 @@ def login(request):
|
||||
login_failed = False
|
||||
|
||||
if request.method == 'POST' and login_form.validate():
|
||||
user = request.db.User.one(
|
||||
user = request.db.User.find_one(
|
||||
{'username': request.POST['username'].lower()})
|
||||
|
||||
if user and user.check_login(request.POST['password']):
|
||||
|
@ -2,6 +2,9 @@
|
||||
# HTML title of the pages
|
||||
html_title = string(default="GNU MediaGoblin")
|
||||
|
||||
# link to source for this MediaGoblin site
|
||||
source_link = string(default="https://gitorious.org/mediagoblin/mediagoblin")
|
||||
|
||||
# Enabled media types
|
||||
media_types = string_list(default=list("mediagoblin.media_types.image"))
|
||||
|
||||
|
@ -155,6 +155,22 @@ def convert_video_media_data(database):
|
||||
collection.save(document)
|
||||
|
||||
@RegisterMigration(11)
|
||||
def convert_gps_media_data(database):
|
||||
"""
|
||||
Move media_data["gps"]["*"] to media_data["gps_*"].
|
||||
In preparation for media_data.gps_*
|
||||
"""
|
||||
collection = database['media_entries']
|
||||
target = collection.find(
|
||||
{'media_data.gps': {'$exists': True}})
|
||||
|
||||
for document in target:
|
||||
for key, value in document['media_data']['gps'].iteritems():
|
||||
document['media_data']['gps_' + key] = value
|
||||
del document['media_data']['gps']
|
||||
collection.save(document)
|
||||
|
||||
@RegisterMigration(12)
|
||||
def user_add_wants_comment_notification(database):
|
||||
"""
|
||||
Add wants_comment_notification to user model
|
||||
|
@ -310,3 +310,9 @@ def check_media_slug_used(db, uploader_id, slug, ignore_m_id):
|
||||
existing_user_slug_entries = db.MediaEntry.find(
|
||||
query_dict).count()
|
||||
return existing_user_slug_entries
|
||||
|
||||
|
||||
def media_entries_for_tag_slug(db, tag_slug):
|
||||
return db.MediaEntry.find(
|
||||
{u'state': u'processed',
|
||||
u'tags.slug': tag_slug})
|
||||
|
@ -14,12 +14,14 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from copy import copy
|
||||
|
||||
from mediagoblin.init import setup_global_and_app_config, setup_database
|
||||
from mediagoblin.db.mongo.util import ObjectId
|
||||
|
||||
from mediagoblin.db.sql.models import (Base, User, MediaEntry, MediaComment,
|
||||
Tag, MediaTag, MediaFile, MediaAttachmentFile)
|
||||
Tag, MediaTag, MediaFile, MediaAttachmentFile, MigrationData)
|
||||
from mediagoblin.media_types.image.models import ImageData
|
||||
from mediagoblin.media_types.video.models import VideoData
|
||||
from mediagoblin.db.sql.open import setup_connection_and_db_from_config as \
|
||||
sql_connect
|
||||
@ -106,6 +108,38 @@ def convert_media_entries(mk_db):
|
||||
session.close()
|
||||
|
||||
|
||||
def convert_image(mk_db):
|
||||
session = Session()
|
||||
|
||||
for media in mk_db.MediaEntry.find(
|
||||
{'media_type': 'mediagoblin.media_types.image'}).sort('created'):
|
||||
media_data = copy(media.media_data)
|
||||
|
||||
# TODO: Fix after exif is migrated
|
||||
media_data.pop('exif', None)
|
||||
|
||||
if len(media_data):
|
||||
media_data_row = ImageData(**media_data)
|
||||
media_data_row.media_entry = obj_id_table[media._id]
|
||||
session.add(media_data_row)
|
||||
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
|
||||
def convert_video(mk_db):
|
||||
session = Session()
|
||||
|
||||
for media in mk_db.MediaEntry.find(
|
||||
{'media_type': 'mediagoblin.media_types.video'}).sort('created'):
|
||||
media_data_row = VideoData(**media.media_data)
|
||||
media_data_row.media_entry = obj_id_table[media._id]
|
||||
session.add(media_data_row)
|
||||
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
|
||||
def convert_media_tags(mk_db):
|
||||
session = Session()
|
||||
session.autoflush = False
|
||||
@ -155,6 +189,20 @@ def convert_media_comments(mk_db):
|
||||
session.close()
|
||||
|
||||
|
||||
def convert_add_migration_versions():
|
||||
session = Session()
|
||||
|
||||
for name in ("__main__",
|
||||
"mediagoblin.media_types.image",
|
||||
"mediagoblin.media_types.video",
|
||||
):
|
||||
m = MigrationData(name=name, version=0)
|
||||
session.add(m)
|
||||
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
|
||||
def run_conversion(config_name):
|
||||
global_config, app_config = setup_global_and_app_config(config_name)
|
||||
|
||||
@ -167,10 +215,16 @@ def run_conversion(config_name):
|
||||
Session.remove()
|
||||
convert_media_entries(mk_db)
|
||||
Session.remove()
|
||||
convert_image(mk_db)
|
||||
Session.remove()
|
||||
convert_video(mk_db)
|
||||
Session.remove()
|
||||
convert_media_tags(mk_db)
|
||||
Session.remove()
|
||||
convert_media_comments(mk_db)
|
||||
Session.remove()
|
||||
convert_add_migration_versions()
|
||||
Session.remove()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -20,6 +20,7 @@ TODO: indexes on foreignkeys, where useful.
|
||||
|
||||
|
||||
import datetime
|
||||
import sys
|
||||
|
||||
from sqlalchemy import (
|
||||
Column, Integer, Unicode, UnicodeText, DateTime, Boolean, ForeignKey,
|
||||
@ -28,10 +29,12 @@ from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
from sqlalchemy.sql.expression import desc
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy.util import memoized_property
|
||||
|
||||
from mediagoblin.db.sql.extratypes import PathTupleWithSlashes, JSONEncoded
|
||||
from mediagoblin.db.sql.base import Base, DictReadAttrProxy
|
||||
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin
|
||||
from mediagoblin.db.sql.base import Session
|
||||
|
||||
# It's actually kind of annoying how sqlalchemy-migrate does this, if
|
||||
# I understand it right, but whatever. Anyway, don't remove this :P
|
||||
@ -58,7 +61,7 @@ class User(Base, UserMixin):
|
||||
TODO: We should consider moving some rarely used fields
|
||||
into some sort of "shadow" table.
|
||||
"""
|
||||
__tablename__ = "users"
|
||||
__tablename__ = "core__users"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
username = Column(Unicode, nullable=False, unique=True)
|
||||
@ -85,10 +88,10 @@ class MediaEntry(Base, MediaEntryMixin):
|
||||
"""
|
||||
TODO: Consider fetching the media_files using join
|
||||
"""
|
||||
__tablename__ = "media_entries"
|
||||
__tablename__ = "core__media_entries"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
uploader = Column(Integer, ForeignKey('users.id'), nullable=False)
|
||||
uploader = Column(Integer, ForeignKey('core__users.id'), nullable=False)
|
||||
title = Column(Unicode, nullable=False)
|
||||
slug = Column(Unicode)
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||
@ -168,14 +171,39 @@ class MediaEntry(Base, MediaEntryMixin):
|
||||
if media is not None:
|
||||
return media.url_for_self(urlgen)
|
||||
|
||||
#@memoized_property
|
||||
@property
|
||||
def media_data(self):
|
||||
# TODO: Replace with proper code to read the correct table
|
||||
return {}
|
||||
session = Session()
|
||||
|
||||
return session.query(self.media_data_table).filter_by(
|
||||
media_entry=self.id).first()
|
||||
|
||||
def media_data_init(self, **kwargs):
|
||||
# TODO: Implement this
|
||||
pass
|
||||
"""
|
||||
Initialize or update the contents of a media entry's media_data row
|
||||
"""
|
||||
session = Session()
|
||||
|
||||
media_data = session.query(self.media_data_table).filter_by(
|
||||
media_entry=self.id).first()
|
||||
|
||||
# No media data, so actually add a new one
|
||||
if not media_data:
|
||||
media_data = self.media_data_table(
|
||||
**kwargs)
|
||||
session.add(media_data)
|
||||
# Update old media data
|
||||
else:
|
||||
for field, value in kwargs.iteritems():
|
||||
setattr(media_data, field, value)
|
||||
|
||||
@memoized_property
|
||||
def media_data_table(self):
|
||||
# TODO: memoize this
|
||||
models_module = self.media_type + '.models'
|
||||
__import__(models_module)
|
||||
return sys.modules[models_module].DATA_MODEL
|
||||
|
||||
|
||||
class FileKeynames(Base):
|
||||
@ -203,7 +231,7 @@ class MediaFile(Base):
|
||||
TODO: Highly consider moving "name" into a new table.
|
||||
TODO: Consider preloading said table in software
|
||||
"""
|
||||
__tablename__ = "mediafiles"
|
||||
__tablename__ = "core__mediafiles"
|
||||
|
||||
media_entry = Column(
|
||||
Integer, ForeignKey(MediaEntry.id),
|
||||
@ -242,7 +270,7 @@ class MediaAttachmentFile(Base):
|
||||
|
||||
|
||||
class Tag(Base):
|
||||
__tablename__ = "tags"
|
||||
__tablename__ = "core__tags"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
slug = Column(Unicode, nullable=False, unique=True)
|
||||
@ -259,13 +287,13 @@ class Tag(Base):
|
||||
|
||||
|
||||
class MediaTag(Base):
|
||||
__tablename__ = "media_tags"
|
||||
__tablename__ = "core__media_tags"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
media_entry = Column(
|
||||
Integer, ForeignKey(MediaEntry.id),
|
||||
nullable=False)
|
||||
tag = Column(Integer, ForeignKey('tags.id'), nullable=False)
|
||||
tag = Column(Integer, ForeignKey('core__tags.id'), nullable=False)
|
||||
name = Column(Unicode)
|
||||
# created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||
|
||||
@ -292,12 +320,12 @@ class MediaTag(Base):
|
||||
|
||||
|
||||
class MediaComment(Base, MediaCommentMixin):
|
||||
__tablename__ = "media_comments"
|
||||
__tablename__ = "core__media_comments"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
media_entry = Column(
|
||||
Integer, ForeignKey('media_entries.id'), nullable=False)
|
||||
author = Column(Integer, ForeignKey('users.id'), nullable=False)
|
||||
Integer, ForeignKey('core__media_entries.id'), nullable=False)
|
||||
author = Column(Integer, ForeignKey('core__users.id'), nullable=False)
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||
content = Column(UnicodeText, nullable=False)
|
||||
|
||||
@ -319,7 +347,7 @@ MODELS = [
|
||||
######################################################
|
||||
|
||||
class MigrationData(Base):
|
||||
__tablename__ = "migrations"
|
||||
__tablename__ = "core__migrations"
|
||||
|
||||
name = Column(Unicode, primary_key=True)
|
||||
version = Column(Integer, nullable=False, default=0)
|
||||
|
@ -294,6 +294,15 @@ def check_media_slug_used(dummy_db, uploader_id, slug, ignore_m_id):
|
||||
return does_exist
|
||||
|
||||
|
||||
def media_entries_for_tag_slug(dummy_db, tag_slug):
|
||||
return MediaEntry.query \
|
||||
.join(MediaEntry.tags_helper) \
|
||||
.join(MediaTag.tag_helper) \
|
||||
.filter(
|
||||
(MediaEntry.state == u'processed')
|
||||
& (Tag.slug == tag_slug))
|
||||
|
||||
|
||||
def clean_orphan_tags():
|
||||
q1 = Session.query(Tag).outerjoin(MediaTag).filter(MediaTag.id==None)
|
||||
for t in q1:
|
||||
|
@ -21,7 +21,9 @@ except ImportError:
|
||||
|
||||
if use_sql:
|
||||
from mediagoblin.db.sql.fake import ObjectId, InvalidId, DESCENDING
|
||||
from mediagoblin.db.sql.util import atomic_update, check_media_slug_used
|
||||
from mediagoblin.db.sql.util import atomic_update, check_media_slug_used, \
|
||||
media_entries_for_tag_slug
|
||||
else:
|
||||
from mediagoblin.db.mongo.util import \
|
||||
ObjectId, InvalidId, DESCENDING, atomic_update, check_media_slug_used
|
||||
ObjectId, InvalidId, DESCENDING, atomic_update, \
|
||||
check_media_slug_used, media_entries_for_tag_slug
|
||||
|
@ -17,7 +17,7 @@
|
||||
import sys
|
||||
|
||||
from mediagoblin.db.mongo import util as db_util
|
||||
from mediagoblin.db.open import setup_connection_and_db_from_config
|
||||
from mediagoblin.db.mongo.open import setup_connection_and_db_from_config
|
||||
from mediagoblin.init import setup_global_and_app_config
|
||||
|
||||
# This MUST be imported so as to set up the appropriate migrations!
|
||||
@ -41,7 +41,12 @@ def _print_finished_migration(migration_number, migration_func):
|
||||
|
||||
|
||||
def migrate(args):
|
||||
global_config, app_config = setup_global_and_app_config(args.conf_file)
|
||||
run_migrate(args.conf_file)
|
||||
|
||||
|
||||
def run_migrate(conf_file):
|
||||
global_config, app_config = setup_global_and_app_config(conf_file)
|
||||
|
||||
connection, db = setup_connection_and_db_from_config(
|
||||
app_config, use_pymongo=True)
|
||||
migration_manager = db_util.MigrationManager(db)
|
||||
|
@ -14,12 +14,15 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from mediagoblin.db.sql.convert import run_conversion
|
||||
|
||||
|
||||
def mongosql_parser_setup(subparser):
|
||||
pass
|
||||
|
||||
|
||||
def mongosql(args):
|
||||
# First, make sure our mongo migrations are up to date...
|
||||
from mediagoblin.gmg_commands.migrate import run_migrate
|
||||
run_migrate(args.conf_file)
|
||||
|
||||
from mediagoblin.db.sql.convert import run_conversion
|
||||
run_conversion(args.conf_file)
|
||||
|
@ -22,7 +22,9 @@ from mediagoblin.gmg_commands import util as commands_util
|
||||
|
||||
|
||||
def shell_parser_setup(subparser):
|
||||
pass
|
||||
subparser.add_argument(
|
||||
'--ipython', help='Use ipython',
|
||||
action="store_true")
|
||||
|
||||
|
||||
SHELL_BANNER = """\
|
||||
@ -34,16 +36,42 @@ Available vars:
|
||||
- db: database instance
|
||||
"""
|
||||
|
||||
def py_shell(**user_namespace):
|
||||
"""
|
||||
Run a shell using normal python shell.
|
||||
"""
|
||||
code.interact(
|
||||
banner=SHELL_BANNER,
|
||||
local=user_namespace)
|
||||
|
||||
|
||||
def ipython_shell(**user_namespace):
|
||||
"""
|
||||
Run a shell for the user using ipython.
|
||||
"""
|
||||
try:
|
||||
from IPython import embed
|
||||
except:
|
||||
print "IPython not available... exiting!"
|
||||
return
|
||||
|
||||
embed(
|
||||
banner1=SHELL_BANNER,
|
||||
user_ns=user_namespace)
|
||||
|
||||
|
||||
def shell(args):
|
||||
"""
|
||||
Setup a shell for the user
|
||||
either a normal Python shell
|
||||
or an IPython one
|
||||
"""
|
||||
mgoblin_app = commands_util.setup_app(args)
|
||||
user_namespace = {
|
||||
'mg_globals': mg_globals,
|
||||
'mgoblin_app': commands_util.setup_app(args),
|
||||
'db': mg_globals.database}
|
||||
|
||||
code.interact(
|
||||
banner=SHELL_BANNER,
|
||||
local={
|
||||
'mgoblin_app': mgoblin_app,
|
||||
'mg_globals': mg_globals,
|
||||
'db': mg_globals.database})
|
||||
if args.ipython:
|
||||
ipython_shell(**user_namespace)
|
||||
else:
|
||||
py_shell(**user_namespace)
|
||||
|
@ -14,7 +14,7 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from mediagoblin.db.util import DESCENDING
|
||||
from mediagoblin.db.util import media_entries_for_tag_slug, DESCENDING
|
||||
|
||||
from mediagoblin.tools.pagination import Pagination
|
||||
from mediagoblin.tools.response import render_to_response
|
||||
@ -29,11 +29,16 @@ def _get_tag_name_from_entries(media_entries, tag_slug):
|
||||
"""
|
||||
# ... this is slightly hacky looking :\
|
||||
tag_name = tag_slug
|
||||
if media_entries.count():
|
||||
for tag in media_entries[0]['tags']:
|
||||
|
||||
for entry in media_entries:
|
||||
for tag in entry.tags:
|
||||
if tag['slug'] == tag_slug:
|
||||
tag_name == tag['name']
|
||||
tag_name = tag['name']
|
||||
break
|
||||
break
|
||||
# TODO: Remove after SQL-switch, it's mongo specific
|
||||
if hasattr(media_entries, "rewind"):
|
||||
media_entries.rewind()
|
||||
|
||||
return tag_name
|
||||
|
||||
@ -43,9 +48,7 @@ def tag_listing(request, page):
|
||||
"""'Gallery'/listing for this tag slug"""
|
||||
tag_slug = request.matchdict[u'tag']
|
||||
|
||||
cursor = request.db.MediaEntry.find(
|
||||
{u'state': u'processed',
|
||||
u'tags.slug': tag_slug})
|
||||
cursor = media_entries_for_tag_slug(request.db, tag_slug)
|
||||
cursor = cursor.sort('created', DESCENDING)
|
||||
|
||||
pagination = Pagination(page, cursor)
|
||||
|
@ -23,11 +23,11 @@ from sqlalchemy import (
|
||||
|
||||
|
||||
class AsciiData(Base):
|
||||
__tablename__ = "ascii_data"
|
||||
__tablename__ = "ascii__mediadata"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
media_entry = Column(
|
||||
Integer, ForeignKey('media_entries.id'), nullable=False)
|
||||
Integer, ForeignKey('core__media_entries.id'), nullable=False)
|
||||
|
||||
|
||||
DATA_MODEL = AsciiData
|
||||
|
@ -5,15 +5,17 @@ from sqlalchemy import (
|
||||
|
||||
|
||||
class ImageData(Base):
|
||||
__tablename__ = "image_data"
|
||||
__tablename__ = "image__mediadata"
|
||||
|
||||
# The primary key *and* reference to the main media_entry
|
||||
media_entry = Column(Integer, ForeignKey('media_entries.id'),
|
||||
media_entry = Column(Integer, ForeignKey('core__media_entries.id'),
|
||||
primary_key=True)
|
||||
width = Column(Integer)
|
||||
height = Column(Integer)
|
||||
gps_longitude = Column(Float)
|
||||
gps_latitude = Column(Float)
|
||||
gps_altitude = Column(Float)
|
||||
gps_direction = Column(Float)
|
||||
|
||||
|
||||
DATA_MODEL = ImageData
|
||||
|
@ -115,11 +115,18 @@ def process_image(entry):
|
||||
|
||||
# Insert exif data into database
|
||||
media_data = entry.setdefault('media_data', {})
|
||||
media_data['exif'] = {
|
||||
'clean': clean_exif(exif_tags)}
|
||||
media_data['exif']['useful'] = get_useful(
|
||||
media_data['exif']['clean'])
|
||||
media_data['gps'] = gps_data
|
||||
|
||||
# TODO: Fix for sql media_data, when exif is in sql
|
||||
if media_data is not None:
|
||||
media_data['exif'] = {
|
||||
'clean': clean_exif(exif_tags)}
|
||||
media_data['exif']['useful'] = get_useful(
|
||||
media_data['exif']['clean'])
|
||||
|
||||
if len(gps_data):
|
||||
for key in list(gps_data.keys()):
|
||||
gps_data['gps_' + key] = gps_data.pop(key)
|
||||
entry.media_data_init(**gps_data)
|
||||
|
||||
# clean up workbench
|
||||
workbench.destroy_self()
|
||||
|
@ -22,10 +22,10 @@ from sqlalchemy import (
|
||||
|
||||
|
||||
class VideoData(Base):
|
||||
__tablename__ = "video_data"
|
||||
__tablename__ = "video__mediadata"
|
||||
|
||||
# The primary key *and* reference to the main media_entry
|
||||
media_entry = Column(Integer, ForeignKey('media_entries.id'),
|
||||
media_entry = Column(Integer, ForeignKey('core__media_entries.id'),
|
||||
primary_key=True)
|
||||
width = Column(SmallInteger)
|
||||
height = Column(SmallInteger)
|
||||
|
@ -234,13 +234,14 @@ text-align: center;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
h3.sidedata {
|
||||
border: none;
|
||||
background-color: #212121;
|
||||
border-radius: 4px 4px 0 0;
|
||||
padding: 3px 8px;
|
||||
margin: 20px 0 5px 0;
|
||||
.media_sidebar h3 {
|
||||
font-size: 1em;
|
||||
margin: 0 0 5px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.media_sidebar p {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* forms */
|
||||
|
1
mediagoblin/static/js/extlib/video-js
Symbolic link
1
mediagoblin/static/js/extlib/video-js
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../extlib/video-js
|
@ -82,7 +82,10 @@
|
||||
{% block mediagoblin_footer %}
|
||||
<footer>
|
||||
{% trans -%}
|
||||
Powered by <a href="http://mediagoblin.org">MediaGoblin</a>, a <a href="http://gnu.org/">GNU</a> project
|
||||
Powered by <a href="http://mediagoblin.org">MediaGoblin</a>, a <a href="http://gnu.org/">GNU</a> project.
|
||||
{%- endtrans %}
|
||||
{% trans source_link=app_config['source_link'] -%}
|
||||
Released under the <a href="http://www.fsf.org/licensing/licenses/agpl-3.0.html">AGPL</a>. <a href="{{ source_link }}">Source code</a> available.
|
||||
{%- endtrans %}
|
||||
</footer>
|
||||
{% endblock mediagoblin_footer %}
|
||||
|
@ -18,6 +18,13 @@
|
||||
|
||||
{% extends 'mediagoblin/user_pages/media.html' %}
|
||||
|
||||
{% block mediagoblin_head %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript"
|
||||
src="{{ request.staticdirect('/js/extlib/video-js/video.js') }}"></script>
|
||||
<link href="{{ request.staticdirect('/js/extlib/video-js/video-js.css') }}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block mediagoblin_media %}
|
||||
<div class="video-player" style="position: relative;">
|
||||
<video class="video-js vjs-default-skin"
|
||||
|
@ -172,7 +172,7 @@
|
||||
</div>
|
||||
<div class="media_sidebar">
|
||||
{% trans date=media.created.strftime("%Y-%m-%d") -%}
|
||||
<h3 class="sidedata">Added on</h3>
|
||||
<h3>Added on</h3>
|
||||
<p>{{ date }}</p>
|
||||
{%- endtrans %}
|
||||
{% if media.tags %}
|
||||
|
@ -17,10 +17,10 @@
|
||||
#}
|
||||
|
||||
{% block exif_content %}
|
||||
{% if media.media_data.has_key('exif')
|
||||
and app_config['exif_visible']
|
||||
{% if app_config['exif_visible']
|
||||
and media.media_data.exif is defined
|
||||
and media.media_data.exif.has_key('useful') %}
|
||||
<h3 class="sidedata">EXIF</h3>
|
||||
<h3>EXIF</h3>
|
||||
<table>
|
||||
{% for key, tag in media.media_data.exif.useful.items() %}
|
||||
<tr>
|
||||
|
@ -17,24 +17,27 @@
|
||||
#}
|
||||
|
||||
{% block geolocation_map %}
|
||||
{% if media.media_data.has_key('gps')
|
||||
and app_config['geolocation_map_visible']
|
||||
and media.media_data.gps %}
|
||||
<h3 class="sidedata">Location</h3>
|
||||
{% if app_config['geolocation_map_visible']
|
||||
and media.media_data.gps_latitude is defined
|
||||
and media.media_data.gps_latitude
|
||||
and media.media_data.gps_longitude is defined
|
||||
and media.media_data.gps_longitude %}
|
||||
<h3>{% trans %}Location{% endtrans %}</h3>
|
||||
<div>
|
||||
{% set gps = media.media_data.gps %}
|
||||
{%- set lon = media.media_data.gps_longitude %}
|
||||
{%- set lat = media.media_data.gps_latitude %}
|
||||
{%- set osm_url = "http://openstreetmap.org/?mlat={lat}&mlon={lon}".format(lat=lat, lon=lon) %}
|
||||
<div id="tile-map" style="width: 100%; height: 196px;">
|
||||
<input type="hidden" id="gps-longitude"
|
||||
value="{{ gps.longitude }}" />
|
||||
value="{{ lon }}" />
|
||||
<input type="hidden" id="gps-latitude"
|
||||
value="{{ gps.latitude }}" />
|
||||
value="{{ lat }}" />
|
||||
</div>
|
||||
<p>
|
||||
<small>
|
||||
View on
|
||||
<a href="http://openstreetmap.org/?mlat={{ gps.latitude }}&mlon={{ gps.longitude }}">
|
||||
OpenStreetMap
|
||||
</a>
|
||||
{% trans -%}
|
||||
View on <a href="{{ osm_url }}">OpenStreetMap</a>
|
||||
{%- endtrans %}
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
||||
#}
|
||||
|
||||
{% block license_content -%}
|
||||
<h3 class="sidedata">{% trans %}License{% endtrans %}</h3>
|
||||
<h3>{% trans %}License{% endtrans %}</h3>
|
||||
<p>
|
||||
{% if media.license %}
|
||||
<a href="{{ media.license }}">{{ media.get_license_data().abbreviation }}</a>
|
||||
|
@ -17,7 +17,7 @@
|
||||
#}
|
||||
|
||||
{% block tags_content -%}
|
||||
<h3 class="sidedata">Tagged with</h3>
|
||||
<h3>{% trans %}Tagged with{% endtrans %}</h3>
|
||||
<p>
|
||||
{% for tag in media.tags %}
|
||||
{% if loop.last %}
|
||||
|
@ -180,6 +180,18 @@ class TestSubmission:
|
||||
# Does media entry exist?
|
||||
assert_true(media)
|
||||
|
||||
# Add a comment, so we can test for its deletion later.
|
||||
get_comments = lambda: list(
|
||||
request.db.MediaComment.find({'media_entry': media._id}))
|
||||
assert_false(get_comments())
|
||||
response = self.test_app.post(
|
||||
request.urlgen('mediagoblin.user_pages.media_post_comment',
|
||||
user=self.test_user.username,
|
||||
media=media._id),
|
||||
{'comment_content': 'i love this test'})
|
||||
response.follow()
|
||||
assert_true(get_comments())
|
||||
|
||||
# Do not confirm deletion
|
||||
# ---------------------------------------------------
|
||||
response = self.test_app.post(
|
||||
@ -219,6 +231,9 @@ class TestSubmission:
|
||||
request.db.MediaEntry.find(
|
||||
{'_id': media._id}).count())
|
||||
|
||||
# How about the comment?
|
||||
assert_false(get_comments())
|
||||
|
||||
def test_malicious_uploads(self):
|
||||
# Test non-suppoerted file with non-supported extension
|
||||
# -----------------------------------------------------
|
||||
|
@ -14,8 +14,12 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
from mediagoblin.db.util import ObjectId, InvalidId
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_user_in_request(request):
|
||||
"""
|
||||
Examine a request and tack on a request.user parameter if that's
|
||||
@ -30,12 +34,12 @@ def setup_user_in_request(request):
|
||||
except InvalidId:
|
||||
user = None
|
||||
else:
|
||||
user = request.db.User.one({'_id': oid})
|
||||
user = request.db.User.find_one({'_id': oid})
|
||||
|
||||
if not user:
|
||||
# Something's wrong... this user doesn't exist? Invalidate
|
||||
# this session.
|
||||
print "Killing session for %r" % request.session['user_id']
|
||||
_log.warn("Killing session for user id %r", request.session['user_id'])
|
||||
request.session.invalidate()
|
||||
|
||||
request.user = user
|
||||
|
@ -21,7 +21,7 @@ from mediagoblin.tools.translate import fake_ugettext_passthrough as _
|
||||
|
||||
class MediaCommentForm(wtforms.Form):
|
||||
comment_content = wtforms.TextAreaField(
|
||||
_(''),
|
||||
'',
|
||||
[wtforms.validators.Required()])
|
||||
|
||||
|
||||
|
@ -180,6 +180,10 @@ def media_confirm_delete(request, media):
|
||||
if form.confirm.data is True:
|
||||
username = media.get_uploader.username
|
||||
|
||||
# Delete all the associated comments
|
||||
for comment in media.get_comments():
|
||||
comment.delete()
|
||||
|
||||
# Delete all files on the public storage
|
||||
delete_media_files(media)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user